diff --git a/application/remote_desk/demo/App.cpp b/application/remote_desk/demo/App.cpp deleted file mode 100644 index a4b8f3c..0000000 --- a/application/remote_desk/demo/App.cpp +++ /dev/null @@ -1,85 +0,0 @@ -//********************************************************* -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License (MIT). -// THE SOFTWARE IS PROVIDED AS IS? WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH -// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -//********************************************************* - -#include "pch.h" -#include "App.h" -#include "SimpleCapture.h" - -using namespace winrt; -using namespace Windows::System; -using namespace Windows::Foundation; -using namespace Windows::UI; -using namespace Windows::UI::Composition; -using namespace Windows::Graphics::Capture; - -void App::Initialize(ContainerVisual const &root) { - auto queue = DispatcherQueue::GetForCurrentThread(); - - m_compositor = root.Compositor(); - m_root = m_compositor.CreateContainerVisual(); - m_content = m_compositor.CreateSpriteVisual(); - m_brush = m_compositor.CreateSurfaceBrush(); - - m_root.RelativeSizeAdjustment({1, 1}); - root.Children().InsertAtTop(m_root); - - m_content.AnchorPoint({0.5f, 0.5f}); - m_content.RelativeOffsetAdjustment({0.5f, 0.5f, 0}); - m_content.RelativeSizeAdjustment({1, 1}); - m_content.Size({-80, -80}); - m_content.Brush(m_brush); - m_brush.HorizontalAlignmentRatio(0.5f); - m_brush.VerticalAlignmentRatio(0.5f); - m_brush.Stretch(CompositionStretch::Uniform); - auto shadow = m_compositor.CreateDropShadow(); - shadow.Mask(m_brush); - m_content.Shadow(shadow); - m_root.Children().InsertAtTop(m_content); - - auto d3dDevice = CreateD3DDevice(); - auto dxgiDevice = d3dDevice.as(); - m_device = CreateDirect3DDevice(dxgiDevice.get()); -} - -void App::StartCapture(HWND hwnd) { - if (m_capture) { - m_capture->Close(); - m_capture = nullptr; - } - - auto item = CreateCaptureItemForWindow(hwnd); - - m_capture = std::make_unique(m_device, item); - - auto surface = m_capture->CreateSurface(m_compositor); - m_brush.Surface(surface); - - m_capture->StartCapture(); -} - -void App::StartCapture(HMONITOR hmonitor) { - if (m_capture) { - m_capture->Close(); - m_capture = nullptr; - } - - auto item = CreateCaptureItemForMonitor(hmonitor); - - m_capture = std::make_unique(m_device, item); - - auto surface = m_capture->CreateSurface(m_compositor); - m_brush.Surface(surface); - - m_capture->StartCapture(); -} \ No newline at end of file diff --git a/application/remote_desk/demo/App.h b/application/remote_desk/demo/App.h deleted file mode 100644 index 95cc2de..0000000 --- a/application/remote_desk/demo/App.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -class SimpleCapture; - -class App { -public: - App() {} - ~App() {} - - void Initialize(winrt::Windows::UI::Composition::ContainerVisual const &root); - - void StartCapture(HWND hwnd); - void StartCapture(HMONITOR hmonitor); - -private: - winrt::Windows::UI::Composition::Compositor m_compositor{nullptr}; - winrt::Windows::UI::Composition::ContainerVisual m_root{nullptr}; - winrt::Windows::UI::Composition::SpriteVisual m_content{nullptr}; - winrt::Windows::UI::Composition::CompositionSurfaceBrush m_brush{nullptr}; - - winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice m_device{ - nullptr}; - std::unique_ptr m_capture{nullptr}; -}; \ No newline at end of file diff --git a/application/remote_desk/demo/SimpleCapture.cpp b/application/remote_desk/demo/SimpleCapture.cpp deleted file mode 100644 index ec72bb2..0000000 --- a/application/remote_desk/demo/SimpleCapture.cpp +++ /dev/null @@ -1,217 +0,0 @@ -//********************************************************* -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License (MIT). -// THE SOFTWARE IS PROVIDED AS IS? WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH -// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -//********************************************************* - -#include "SimpleCapture.h" - -#include "pch.h" - -using namespace winrt; -using namespace Windows; -using namespace Windows::Foundation; -using namespace Windows::System; -using namespace Windows::Graphics; -using namespace Windows::Graphics::Capture; -using namespace Windows::Graphics::DirectX; -using namespace Windows::Graphics::DirectX::Direct3D11; -using namespace Windows::Foundation::Numerics; -using namespace Windows::UI; -using namespace Windows::UI::Composition; - -SimpleCapture::SimpleCapture(IDirect3DDevice const &device, - GraphicsCaptureItem const &item) { - m_item = item; - m_device = device; - - // Set up - auto d3dDevice = GetDXGIInterfaceFromObject(m_device); - d3dDevice->GetImmediateContext(m_d3dContext.put()); - - auto size = m_item.Size(); - - m_swapChain = CreateDXGISwapChain( - d3dDevice, static_cast(size.Width), - static_cast(size.Height), - static_cast(DirectXPixelFormat::B8G8R8A8UIntNormalized), 2); - - // Create framepool, define pixel format (DXGI_FORMAT_B8G8R8A8_UNORM), and - // frame size. - m_framePool = Direct3D11CaptureFramePool::Create( - m_device, DirectXPixelFormat::B8G8R8A8UIntNormalized, 2, size); - m_session = m_framePool.CreateCaptureSession(m_item); - m_lastSize = size; - m_frameArrived = m_framePool.FrameArrived( - auto_revoke, {this, &SimpleCapture::OnFrameArrived}); -} - -// Start sending capture frames -void SimpleCapture::StartCapture() { - CheckClosed(); - m_session.StartCapture(); -} - -ICompositionSurface SimpleCapture::CreateSurface(Compositor const &compositor) { - CheckClosed(); - return CreateCompositionSurfaceForSwapChain(compositor, m_swapChain.get()); -} - -// Process captured frames -void SimpleCapture::Close() { - auto expected = false; - if (m_closed.compare_exchange_strong(expected, true)) { - m_frameArrived.revoke(); - m_framePool.Close(); - m_session.Close(); - - m_swapChain = nullptr; - m_framePool = nullptr; - m_session = nullptr; - m_item = nullptr; - } -} - -void SimpleCapture::OnFrameArrived( - Direct3D11CaptureFramePool const &sender, - winrt::Windows::Foundation::IInspectable const &) { - auto newSize = false; - - { - auto frame = sender.TryGetNextFrame(); - auto frameContentSize = frame.ContentSize(); - - if (frameContentSize.Width != m_lastSize.Width || - frameContentSize.Height != m_lastSize.Height) { - // The thing we have been capturing has changed size. - // We need to resize our swap chain first, then blit the pixels. - // After we do that, retire the frame and then recreate our frame pool. - newSize = true; - m_lastSize = frameContentSize; - m_swapChain->ResizeBuffers( - 2, static_cast(m_lastSize.Width), - static_cast(m_lastSize.Height), - static_cast(DirectXPixelFormat::B8G8R8A8UIntNormalized), - 0); - } - - // copy to swapChain - { - auto frameSurface = - GetDXGIInterfaceFromObject(frame.Surface()); - - com_ptr backBuffer; - check_hresult(m_swapChain->GetBuffer(0, guid_of(), - backBuffer.put_void())); - - m_d3dContext->CopyResource(backBuffer.get(), frameSurface.get()); - - DXGI_PRESENT_PARAMETERS presentParameters = {0}; - m_swapChain->Present1(1, 0, &presentParameters); - } - - // copy to mapped texture - { - auto frameSurface = - GetDXGIInterfaceFromObject(frame.Surface()); - - if (!m_mappedTexture || newSize) CreateMappedTexture(frameSurface); - - m_d3dContext->CopyResource(m_mappedTexture.get(), frameSurface.get()); - - D3D11_MAPPED_SUBRESOURCE mapInfo; - m_d3dContext->Map(m_mappedTexture.get(), 0, D3D11_MAP_READ, - D3D11_MAP_FLAG_DO_NOT_WAIT, &mapInfo); - - // copy data from mapInfo.pData -#if 1 - if (mapInfo.pData) { - static unsigned char *buffer = nullptr; - if (buffer && newSize) delete[] buffer; - - if (!buffer) - buffer = new unsigned char[frameContentSize.Width * - frameContentSize.Height * 4]; - - int dstRowPitch = frameContentSize.Width * 4; - for (int h = 0; h < frameContentSize.Height; h++) { - memcpy_s(buffer + h * dstRowPitch, dstRowPitch, - (BYTE *)mapInfo.pData + h * mapInfo.RowPitch, - min(mapInfo.RowPitch, dstRowPitch)); - } - - BITMAPINFOHEADER bi; - - bi.biSize = sizeof(BITMAPINFOHEADER); - bi.biWidth = frameContentSize.Width; - bi.biHeight = frameContentSize.Height * (-1); - bi.biPlanes = 1; - bi.biBitCount = 32; // should get from system color bits - bi.biCompression = BI_RGB; - bi.biSizeImage = 0; - bi.biXPelsPerMeter = 0; - bi.biYPelsPerMeter = 0; - bi.biClrUsed = 0; - bi.biClrImportant = 0; - - BITMAPFILEHEADER bf; - bf.bfType = 0x4d42; - bf.bfReserved1 = 0; - bf.bfReserved2 = 0; - bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); - bf.bfSize = - bf.bfOffBits + frameContentSize.Width * frameContentSize.Height * 4; - - FILE *fp = nullptr; - - fopen_s(&fp, ".\\save.bmp", "wb+"); - - fwrite(&bf, 1, sizeof(bf), fp); - fwrite(&bi, 1, sizeof(bi), fp); - fwrite(buffer, 1, frameContentSize.Width * frameContentSize.Height * 4, - fp); - - fflush(fp); - fclose(fp); - } -#endif - - m_d3dContext->Unmap(m_mappedTexture.get(), 0); - } - } - - if (newSize) { - m_framePool.Recreate(m_device, DirectXPixelFormat::B8G8R8A8UIntNormalized, - 2, m_lastSize); - } -} - -HRESULT -SimpleCapture::CreateMappedTexture(winrt::com_ptr src_texture, - UINT width, UINT height) { - D3D11_TEXTURE2D_DESC src_desc; - src_texture->GetDesc(&src_desc); - D3D11_TEXTURE2D_DESC map_desc; - map_desc.Width = width == 0 ? src_desc.Width : width; - map_desc.Height = height == 0 ? src_desc.Height : height; - map_desc.MipLevels = src_desc.MipLevels; - map_desc.ArraySize = src_desc.ArraySize; - map_desc.Format = src_desc.Format; - map_desc.SampleDesc = src_desc.SampleDesc; - map_desc.Usage = D3D11_USAGE_STAGING; - map_desc.BindFlags = 0; - map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - map_desc.MiscFlags = 0; - - auto d3dDevice = GetDXGIInterfaceFromObject(m_device); - - return d3dDevice->CreateTexture2D(&map_desc, nullptr, m_mappedTexture.put()); -} diff --git a/application/remote_desk/demo/SimpleCapture.h b/application/remote_desk/demo/SimpleCapture.h deleted file mode 100644 index ce66db3..0000000 --- a/application/remote_desk/demo/SimpleCapture.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -class SimpleCapture { -public: - SimpleCapture( - winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice const - &device, - winrt::Windows::Graphics::Capture::GraphicsCaptureItem const &item); - ~SimpleCapture() { Close(); } - - void StartCapture(); - winrt::Windows::UI::Composition::ICompositionSurface - CreateSurface(winrt::Windows::UI::Composition::Compositor const &compositor); - - void Close(); - -private: - void OnFrameArrived( - winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const - &sender, - winrt::Windows::Foundation::IInspectable const &args); - - void CheckClosed() { - if (m_closed.load() == true) { - throw winrt::hresult_error(RO_E_CLOSED); - } - } - - HRESULT - CreateMappedTexture(winrt::com_ptr src_texture, - UINT width = 0, UINT height = 0); - -private: - winrt::Windows::Graphics::Capture::GraphicsCaptureItem m_item{nullptr}; - winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool m_framePool{ - nullptr}; - winrt::Windows::Graphics::Capture::GraphicsCaptureSession m_session{nullptr}; - winrt::Windows::Graphics::SizeInt32 m_lastSize; - - winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice m_device{ - nullptr}; - winrt::com_ptr m_swapChain{nullptr}; - winrt::com_ptr m_d3dContext{nullptr}; - winrt::com_ptr m_mappedTexture{nullptr}; - - std::atomic m_closed = false; - winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool:: - FrameArrived_revoker m_frameArrived; -}; \ No newline at end of file diff --git a/application/remote_desk/demo/Win32MonitorEnumeration.h b/application/remote_desk/demo/Win32MonitorEnumeration.h deleted file mode 100644 index e9e7bca..0000000 --- a/application/remote_desk/demo/Win32MonitorEnumeration.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -#include - -struct Monitor { -public: - Monitor(nullptr_t) {} - Monitor(HMONITOR hmonitor, std::wstring &className, bool isPrimary) { - m_hmonitor = hmonitor; - m_className = className; - m_bIsPrimary = isPrimary; - } - - HMONITOR Hmonitor() const noexcept { return m_hmonitor; } - std::wstring ClassName() const noexcept { return m_className; } - bool IsPrimary() const noexcept { return m_bIsPrimary; } - -private: - HMONITOR m_hmonitor; - std::wstring m_className; - bool m_bIsPrimary; -}; - -BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, HDC hdc, LPRECT lprc, - LPARAM data) { - - MONITORINFOEX info_ex; - info_ex.cbSize = sizeof(MONITORINFOEX); - - GetMonitorInfo(hmonitor, &info_ex); - - if (info_ex.dwFlags == DISPLAY_DEVICE_MIRRORING_DRIVER) - return true; - - auto monitors = ((std::vector *)data); - std::wstring name = info_ex.szDevice; - auto monitor = - Monitor(hmonitor, name, info_ex.dwFlags & MONITORINFOF_PRIMARY); - - monitors->emplace_back(monitor); - - return true; -} - -std::vector EnumerateMonitors() { - std::vector monitors; - - ::EnumDisplayMonitors(NULL, NULL, EnumMonitorProc, (LPARAM)&monitors); - - return monitors; -} \ No newline at end of file diff --git a/application/remote_desk/demo/Win32WindowEnumeration.h b/application/remote_desk/demo/Win32WindowEnumeration.h deleted file mode 100644 index 20761c2..0000000 --- a/application/remote_desk/demo/Win32WindowEnumeration.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once -#include - -struct Window { -public: - Window(nullptr_t) {} - Window(HWND hwnd, std::wstring const &title, std::wstring &className) { - m_hwnd = hwnd; - m_title = title; - m_className = className; - } - - HWND Hwnd() const noexcept { return m_hwnd; } - std::wstring Title() const noexcept { return m_title; } - std::wstring ClassName() const noexcept { return m_className; } - -private: - HWND m_hwnd; - std::wstring m_title; - std::wstring m_className; -}; - -std::wstring GetClassName(HWND hwnd) { - std::array className; - - ::GetClassName(hwnd, className.data(), (int)className.size()); - - std::wstring title(className.data()); - return title; -} - -std::wstring GetWindowText(HWND hwnd) { - std::array windowText; - - ::GetWindowText(hwnd, windowText.data(), (int)windowText.size()); - - std::wstring title(windowText.data()); - return title; -} - -bool IsAltTabWindow(Window const &window) { - HWND hwnd = window.Hwnd(); - HWND shellWindow = GetShellWindow(); - - auto title = window.Title(); - auto className = window.ClassName(); - - if (hwnd == shellWindow) { - return false; - } - - if (title.length() == 0) { - return false; - } - - if (!IsWindowVisible(hwnd)) { - return false; - } - - if (GetAncestor(hwnd, GA_ROOT) != hwnd) { - return false; - } - - LONG style = GetWindowLong(hwnd, GWL_STYLE); - if (!((style & WS_DISABLED) != WS_DISABLED)) { - return false; - } - - DWORD cloaked = FALSE; - HRESULT hrTemp = - DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloaked, sizeof(cloaked)); - if (SUCCEEDED(hrTemp) && cloaked == DWM_CLOAKED_SHELL) { - return false; - } - - return true; -} - -BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) { - auto class_name = GetClassName(hwnd); - auto title = GetWindowText(hwnd); - - auto window = Window(hwnd, title, class_name); - - if (!IsAltTabWindow(window)) { - return TRUE; - } - - std::vector &windows = - *reinterpret_cast *>(lParam); - windows.push_back(window); - - return TRUE; -} - -const std::vector EnumerateWindows() { - std::vector windows; - EnumWindows(EnumWindowsProc, reinterpret_cast(&windows)); - - return windows; -} \ No newline at end of file diff --git a/application/remote_desk/demo/capture.interop.h b/application/remote_desk/demo/capture.interop.h deleted file mode 100644 index 2232672..0000000 --- a/application/remote_desk/demo/capture.interop.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include -#include -#include - -inline auto CreateCaptureItemForWindow(HWND hwnd) { - auto activation_factory = winrt::get_activation_factory< - winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); - auto interop_factory = activation_factory.as(); - winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; - interop_factory->CreateForWindow( - hwnd, - winrt::guid_of(), - reinterpret_cast(winrt::put_abi(item))); - return item; -} - -inline auto CreateCaptureItemForMonitor(HMONITOR hmonitor) { - auto activation_factory = winrt::get_activation_factory< - winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); - auto interop_factory = activation_factory.as(); - winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; - interop_factory->CreateForMonitor( - hmonitor, - winrt::guid_of(), - reinterpret_cast(winrt::put_abi(item))); - return item; -} \ No newline at end of file diff --git a/application/remote_desk/demo/composition.interop.h b/application/remote_desk/demo/composition.interop.h deleted file mode 100644 index 01a10ce..0000000 --- a/application/remote_desk/demo/composition.interop.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include -#include -#include - -inline auto CreateCompositionGraphicsDevice( - winrt::Windows::UI::Composition::Compositor const &compositor, - ::IUnknown *device) { - winrt::Windows::UI::Composition::CompositionGraphicsDevice graphicsDevice{ - nullptr}; - auto compositorInterop = - compositor.as(); - winrt::com_ptr - graphicsInterop; - winrt::check_hresult( - compositorInterop->CreateGraphicsDevice(device, graphicsInterop.put())); - winrt::check_hresult(graphicsInterop->QueryInterface( - winrt::guid_of< - winrt::Windows::UI::Composition::CompositionGraphicsDevice>(), - reinterpret_cast(winrt::put_abi(graphicsDevice)))); - return graphicsDevice; -} - -inline void ResizeSurface( - winrt::Windows::UI::Composition::CompositionDrawingSurface const &surface, - winrt::Windows::Foundation::Size const &size) { - auto surfaceInterop = surface.as< - ABI::Windows::UI::Composition::ICompositionDrawingSurfaceInterop>(); - SIZE newSize = {}; - newSize.cx = static_cast(std::round(size.Width)); - newSize.cy = static_cast(std::round(size.Height)); - winrt::check_hresult(surfaceInterop->Resize(newSize)); -} - -inline auto SurfaceBeginDraw( - winrt::Windows::UI::Composition::CompositionDrawingSurface const &surface) { - auto surfaceInterop = surface.as< - ABI::Windows::UI::Composition::ICompositionDrawingSurfaceInterop>(); - winrt::com_ptr context; - POINT offset = {}; - winrt::check_hresult(surfaceInterop->BeginDraw( - nullptr, __uuidof(ID2D1DeviceContext), context.put_void(), &offset)); - context->SetTransform( - D2D1::Matrix3x2F::Translation((FLOAT)offset.x, (FLOAT)offset.y)); - return context; -} - -inline void SurfaceEndDraw( - winrt::Windows::UI::Composition::CompositionDrawingSurface const &surface) { - auto surfaceInterop = surface.as< - ABI::Windows::UI::Composition::ICompositionDrawingSurfaceInterop>(); - winrt::check_hresult(surfaceInterop->EndDraw()); -} - -inline auto CreateCompositionSurfaceForSwapChain( - winrt::Windows::UI::Composition::Compositor const &compositor, - ::IUnknown *swapChain) { - winrt::Windows::UI::Composition::ICompositionSurface surface{nullptr}; - auto compositorInterop = - compositor.as(); - winrt::com_ptr - surfaceInterop; - winrt::check_hresult(compositorInterop->CreateCompositionSurfaceForSwapChain( - swapChain, surfaceInterop.put())); - winrt::check_hresult(surfaceInterop->QueryInterface( - winrt::guid_of(), - reinterpret_cast(winrt::put_abi(surface)))); - return surface; -} \ No newline at end of file diff --git a/application/remote_desk/demo/d3dHelpers.h b/application/remote_desk/demo/d3dHelpers.h deleted file mode 100644 index c5d87f5..0000000 --- a/application/remote_desk/demo/d3dHelpers.h +++ /dev/null @@ -1,132 +0,0 @@ -#pragma once -#include "composition.interop.h" - -struct SurfaceContext { -public: - SurfaceContext(std::nullptr_t) {} - SurfaceContext( - winrt::Windows::UI::Composition::CompositionDrawingSurface surface) { - m_surface = surface; - m_d2dContext = SurfaceBeginDraw(m_surface); - } - ~SurfaceContext() { - SurfaceEndDraw(m_surface); - m_d2dContext = nullptr; - m_surface = nullptr; - } - - winrt::com_ptr GetDeviceContext() { return m_d2dContext; } - -private: - winrt::com_ptr m_d2dContext; - winrt::Windows::UI::Composition::CompositionDrawingSurface m_surface{nullptr}; -}; - -struct D3D11DeviceLock { -public: - D3D11DeviceLock(std::nullopt_t) {} - D3D11DeviceLock(ID3D11Multithread *pMultithread) { - m_multithread.copy_from(pMultithread); - m_multithread->Enter(); - } - ~D3D11DeviceLock() { - m_multithread->Leave(); - m_multithread = nullptr; - } - -private: - winrt::com_ptr m_multithread; -}; - -inline auto CreateWICFactory() { - winrt::com_ptr wicFactory; - winrt::check_hresult(::CoCreateInstance( - CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, - winrt::guid_of(), wicFactory.put_void())); - - return wicFactory; -} - -inline auto CreateD2DDevice(winrt::com_ptr const &factory, - winrt::com_ptr const &device) { - winrt::com_ptr result; - winrt::check_hresult( - factory->CreateDevice(device.as().get(), result.put())); - return result; -} - -inline auto CreateD3DDevice(D3D_DRIVER_TYPE const type, - winrt::com_ptr &device) { - WINRT_ASSERT(!device); - - UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - - //#ifdef _DEBUG - // flags |= D3D11_CREATE_DEVICE_DEBUG; - //#endif - - return D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0, - D3D11_SDK_VERSION, device.put(), nullptr, nullptr); -} - -inline auto CreateD3DDevice() { - winrt::com_ptr device; - HRESULT hr = CreateD3DDevice(D3D_DRIVER_TYPE_HARDWARE, device); - - if (DXGI_ERROR_UNSUPPORTED == hr) { - hr = CreateD3DDevice(D3D_DRIVER_TYPE_WARP, device); - } - - winrt::check_hresult(hr); - return device; -} - -inline auto CreateD2DFactory() { - D2D1_FACTORY_OPTIONS options{}; - - //#ifdef _DEBUG - // options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; - //#endif - - winrt::com_ptr factory; - - winrt::check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, - options, factory.put())); - - return factory; -} - -inline auto CreateDXGISwapChain(winrt::com_ptr const &device, - const DXGI_SWAP_CHAIN_DESC1 *desc) { - auto dxgiDevice = device.as(); - winrt::com_ptr adapter; - winrt::check_hresult(dxgiDevice->GetParent(winrt::guid_of(), - adapter.put_void())); - winrt::com_ptr factory; - winrt::check_hresult( - adapter->GetParent(winrt::guid_of(), factory.put_void())); - - winrt::com_ptr swapchain; - winrt::check_hresult(factory->CreateSwapChainForComposition( - device.get(), desc, nullptr, swapchain.put())); - - return swapchain; -} - -inline auto CreateDXGISwapChain(winrt::com_ptr const &device, - uint32_t width, uint32_t height, - DXGI_FORMAT format, uint32_t bufferCount) { - DXGI_SWAP_CHAIN_DESC1 desc = {}; - desc.Width = width; - desc.Height = height; - desc.Format = format; - desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - desc.SampleDesc.Count = 1; - desc.SampleDesc.Quality = 0; - desc.BufferCount = bufferCount; - desc.Scaling = DXGI_SCALING_STRETCH; - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; - desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; - - return CreateDXGISwapChain(device, &desc); -} \ No newline at end of file diff --git a/application/remote_desk/demo/direct3d11.interop.h b/application/remote_desk/demo/direct3d11.interop.h deleted file mode 100644 index d282bbe..0000000 --- a/application/remote_desk/demo/direct3d11.interop.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include - -extern "C" { -HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice( - ::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice); - -HRESULT __stdcall CreateDirect3D11SurfaceFromDXGISurface( - ::IDXGISurface *dgxiSurface, ::IInspectable **graphicsSurface); -} - -struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")) - IDirect3DDxgiInterfaceAccess : ::IUnknown { - virtual HRESULT __stdcall GetInterface(GUID const &id, void **object) = 0; -}; - -inline auto CreateDirect3DDevice(IDXGIDevice *dxgi_device) { - winrt::com_ptr<::IInspectable> d3d_device; - winrt::check_hresult( - CreateDirect3D11DeviceFromDXGIDevice(dxgi_device, d3d_device.put())); - return d3d_device - .as(); -} - -inline auto CreateDirect3DSurface(IDXGISurface *dxgi_surface) { - winrt::com_ptr<::IInspectable> d3d_surface; - winrt::check_hresult( - CreateDirect3D11SurfaceFromDXGISurface(dxgi_surface, d3d_surface.put())); - return d3d_surface - .as(); -} - -template -auto GetDXGIInterfaceFromObject( - winrt::Windows::Foundation::IInspectable const &object) { - auto access = object.as(); - winrt::com_ptr result; - winrt::check_hresult( - access->GetInterface(winrt::guid_of(), result.put_void())); - return result; -} \ No newline at end of file diff --git a/application/remote_desk/demo/main.cpp b/application/remote_desk/demo/main.cpp deleted file mode 100644 index 9f0e68d..0000000 --- a/application/remote_desk/demo/main.cpp +++ /dev/null @@ -1,160 +0,0 @@ -//********************************************************* -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License (MIT). -// THE SOFTWARE IS PROVIDED AS IS? WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH -// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -//********************************************************* - -#include "pch.h" -#include "App.h" -#include "SimpleCapture.h" -#include "Win32MonitorEnumeration.h" -#include "Win32WindowEnumeration.h" -#include - -using namespace winrt; -using namespace Windows::UI; -using namespace Windows::UI::Composition; -using namespace Windows::UI::Composition::Desktop; - -// Direct3D11CaptureFramePool requires a DispatcherQueue -auto CreateDispatcherQueueController() { - namespace abi = ABI::Windows::System; - - DispatcherQueueOptions options{sizeof(DispatcherQueueOptions), - DQTYPE_THREAD_CURRENT, DQTAT_COM_STA}; - - Windows::System::DispatcherQueueController controller{nullptr}; - check_hresult(CreateDispatcherQueueController( - options, reinterpret_cast( - put_abi(controller)))); - return controller; -} - -DesktopWindowTarget CreateDesktopWindowTarget(Compositor const &compositor, - HWND window) { - namespace abi = ABI::Windows::UI::Composition::Desktop; - - auto interop = compositor.as(); - DesktopWindowTarget target{nullptr}; - check_hresult(interop->CreateDesktopWindowTarget( - window, true, - reinterpret_cast(put_abi(target)))); - return target; -} - -int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, - LPSTR cmdLine, int cmdShow); - -auto g_app = std::make_shared(); -auto g_windows = EnumerateWindows(); -auto g_monitors = EnumerateMonitors(); - -LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); - -int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, - LPSTR cmdLine, int cmdShow) { - // Init COM - init_apartment(apartment_type::single_threaded); - - // Create the window - WNDCLASSEX wcex = {}; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = instance; - wcex.hIcon = LoadIcon(instance, MAKEINTRESOURCE(IDI_APPLICATION)); - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wcex.lpszMenuName = NULL; - wcex.lpszClassName = L"ScreenCaptureforHWND"; - wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); - WINRT_VERIFY(RegisterClassEx(&wcex)); - - HWND hwnd = CreateWindow(L"ScreenCaptureforHWND", L"ScreenCaptureforHWND", - WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, - 800, 600, NULL, NULL, instance, NULL); - WINRT_VERIFY(hwnd); - - ShowWindow(hwnd, cmdShow); - UpdateWindow(hwnd); - - // Create combo box - HWND comboBoxHwnd = - CreateWindow(WC_COMBOBOX, L"", - CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_CHILD | - WS_OVERLAPPED | WS_VISIBLE, - 10, 10, 200, 200, hwnd, NULL, instance, NULL); - WINRT_VERIFY(comboBoxHwnd); - - // Populate combo box - // for (auto &window : g_windows) { - // SendMessage(comboBoxHwnd, CB_ADDSTRING, 0, (LPARAM)window.Title().c_str()); - // } - - for (auto &monitor : g_monitors) { - SendMessage(comboBoxHwnd, CB_ADDSTRING, 0, - (LPARAM)monitor.ClassName().c_str()); - } - - // SendMessage(comboBoxHwnd, CB_SETCURSEL, 0, 0); - - // Create a DispatcherQueue for our thread - auto controller = CreateDispatcherQueueController(); - - // Initialize Composition - auto compositor = Compositor(); - auto target = CreateDesktopWindowTarget(compositor, hwnd); - auto root = compositor.CreateContainerVisual(); - root.RelativeSizeAdjustment({1.0f, 1.0f}); - target.Root(root); - - // Enqueue our capture work on the dispatcher - auto queue = controller.DispatcherQueue(); - auto success = queue.TryEnqueue([=]() -> void { g_app->Initialize(root); }); - WINRT_VERIFY(success); - - // Message pump - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - return (int)msg.wParam; -} - -LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) { - case WM_DESTROY: - PostQuitMessage(0); - break; - case WM_COMMAND: - if (HIWORD(wParam) == CBN_SELCHANGE) { - auto index = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); - // if (index < g_windows.size() - 1) { - // auto window = g_windows[index]; - // g_app->StartCapture(window.Hwnd()); - // } else { - // auto monitor = g_monitors[index - g_windows.size()]; - auto monitor = g_monitors[0]; - g_app->StartCapture(monitor.Hmonitor()); - // } - } - break; - default: - return DefWindowProc(hwnd, msg, wParam, lParam); - break; - } - - return 0; -} \ No newline at end of file diff --git a/application/remote_desk/demo/pch.cpp b/application/remote_desk/demo/pch.cpp deleted file mode 100644 index 1730571..0000000 --- a/application/remote_desk/demo/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" \ No newline at end of file diff --git a/application/remote_desk/demo/pch.h b/application/remote_desk/demo/pch.h deleted file mode 100644 index 54bdc4a..0000000 --- a/application/remote_desk/demo/pch.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include - -// WinRT -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -// STL -#include -#include - -// D3D -#include -#include -#include -#include - -// Helpers -#include "composition.interop.h" -#include "d3dHelpers.h" -#include "direct3d11.interop.h" -#include "capture.interop.h" \ No newline at end of file diff --git a/application/remote_desk/remote_desk.cpp.bak b/application/remote_desk/remote_desk.cpp.bak deleted file mode 100644 index 1e0b40b..0000000 --- a/application/remote_desk/remote_desk.cpp.bak +++ /dev/null @@ -1,368 +0,0 @@ -#include - -#define __STDC_CONSTANT_MACROS - -#ifdef _WIN32 -// Windows -extern "C" { -#include -#include -#include -#include -#include - -#include "SDL2/SDL.h" -}; -#else -// Linux... -#ifdef __cplusplus -extern "C" { -#endif -#include -#include -#include -#include -#include -#ifdef __cplusplus -}; -#endif -#endif - -// Output YUV420P -#define OUTPUT_YUV420P 0 -//'1' Use Dshow -//'0' Use GDIgrab -#define USE_DSHOW 0 - -// Refresh Event -#define SFM_REFRESH_EVENT (SDL_USEREVENT + 1) - -#define SFM_BREAK_EVENT (SDL_USEREVENT + 2) - -int thread_exit = 0; -SDL_Texture *sdlTexture = nullptr; -SDL_Renderer *sdlRenderer = nullptr; -SDL_Rect sdlRect; - -int YUV420ToNV12FFmpeg(unsigned char *src_buffer, int width, int height, - unsigned char *des_buffer) { - AVFrame *Input_pFrame = av_frame_alloc(); - AVFrame *Output_pFrame = av_frame_alloc(); - struct SwsContext *img_convert_ctx = sws_getContext( - width, height, AV_PIX_FMT_NV12, width, height, AV_PIX_FMT_YUV420P, - SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); - - av_image_fill_arrays(Input_pFrame->data, Input_pFrame->linesize, src_buffer, - AV_PIX_FMT_NV12, width, height, 1); - av_image_fill_arrays(Output_pFrame->data, Output_pFrame->linesize, des_buffer, - AV_PIX_FMT_YUV420P, width, height, 1); - - sws_scale(img_convert_ctx, (uint8_t const **)Input_pFrame->data, - Input_pFrame->linesize, 0, height, Output_pFrame->data, - Output_pFrame->linesize); - - if (Input_pFrame) av_free(Input_pFrame); - if (Output_pFrame) av_free(Output_pFrame); - if (img_convert_ctx) sws_freeContext(img_convert_ctx); - - return 0; -} - -int sfp_refresh_thread(void *opaque) { - thread_exit = 0; - while (!thread_exit) { - SDL_Event event; - event.type = SFM_REFRESH_EVENT; - SDL_PushEvent(&event); - SDL_Delay(40); - } - thread_exit = 0; - // Break - SDL_Event event; - event.type = SFM_BREAK_EVENT; - SDL_PushEvent(&event); - - return 0; -} - -// Show Dshow Device -// void show_dshow_device() { -// AVFormatContext *pFormatCtx = avformat_alloc_context(); -// AVDictionary *options = NULL; -// av_dict_set(&options, "list_devices", "true", 0); -// AVInputFormat *iformat = av_find_input_format("dshow"); -// printf("========Device Info=============\n"); -// avformat_open_input(&pFormatCtx, "video=dummy", iformat, &options); -// printf("================================\n"); -// } - -// Show AVFoundation Device -void show_avfoundation_device() { - const AVFormatContext *const_pFormatCtx = avformat_alloc_context(); - AVFormatContext *pFormatCtx = const_cast(const_pFormatCtx); - - AVDictionary *options = NULL; - av_dict_set(&options, "list_devices", "true", 0); - const AVInputFormat *const_iformat = av_find_input_format("avfoundation"); - AVInputFormat *iformat = const_cast(const_iformat); - - printf("==AVFoundation Device Info===\n"); - avformat_open_input(&pFormatCtx, "", iformat, &options); - printf("=============================\n"); -} - -int main(int argc, char *argv[]) { - AVFormatContext *pFormatCtx; - int i, videoindex; - AVCodecContext *pCodecCtx; - AVCodec *pCodec; - - avformat_network_init(); - pFormatCtx = avformat_alloc_context(); - - // Open File - // char filepath[]="src01_480x272_22.h265"; - // avformat_open_input(&pFormatCtx,filepath,NULL,NULL) - - // Register Device - avdevice_register_all(); - // Windows -#ifdef _WIN32 -#if USE_DSHOW - // Use dshow - // - // Need to Install screen-capture-recorder - // screen-capture-recorder - // Website: http://sourceforge.net/projects/screencapturer/ - // - AVInputFormat *ifmt = av_find_input_format("dshow"); - if (avformat_open_input(&pFormatCtx, "video=screen-capture-recorder", ifmt, - NULL) != 0) { - printf("Couldn't open input stream.\n"); - return -1; - } -#else - // Use gdigrab - AVDictionary *options = NULL; - // Set some options - // grabbing frame rate - // av_dict_set(&options,"framerate","5",0); - // The distance from the left edge of the screen or desktop - // av_dict_set(&options,"offset_x","20",0); - // The distance from the top edge of the screen or desktop - // av_dict_set(&options,"offset_y","40",0); - // Video frame size. The default is to capture the full screen - // av_dict_set(&options,"video_size","640x480",0); - const AVInputFormat *ifmt = av_find_input_format("gdigrab"); - if (avformat_open_input(&pFormatCtx, "desktop", ifmt, &options) != 0) { - printf("Couldn't open input stream.\n"); - return -1; - } - -#endif -#elif defined linux - // Linux - AVDictionary *options = NULL; - // Set some options - // grabbing frame rate - // av_dict_set(&options,"framerate","5",0); - // Make the grabbed area follow the mouse - // av_dict_set(&options,"follow_mouse","centered",0); - // Video frame size. The default is to capture the full screen - // av_dict_set(&options,"video_size","640x480",0); - AVInputFormat *ifmt = av_find_input_format("x11grab"); - // Grab at position 10,20 - if (avformat_open_input(&pFormatCtx, ":0.0+10,20", ifmt, &options) != 0) { - printf("Couldn't open input stream.\n"); - return -1; - } -#else - show_avfoundation_device(); - // Mac - AVInputFormat *ifmt = av_find_input_format("avfoundation"); - // Avfoundation - //[video]:[audio] - if (avformat_open_input(&pFormatCtx, "1", ifmt, NULL) != 0) { - printf("Couldn't open input stream.\n"); - return -1; - } -#endif - - if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { - printf("Couldn't find stream information.\n"); - return -1; - } - videoindex = -1; - for (i = 0; i < pFormatCtx->nb_streams; i++) - if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - videoindex = i; - break; - } - if (videoindex == -1) { - printf("Didn't find a video stream.\n"); - return -1; - } - // pCodecCtx = pFormatCtx->streams[videoindex]->codec; - - pCodecCtx = avcodec_alloc_context3(NULL); - avcodec_parameters_to_context(pCodecCtx, - pFormatCtx->streams[videoindex]->codecpar); - - pCodec = const_cast(avcodec_find_decoder(pCodecCtx->codec_id)); - // pCodec = avcodec_find_decoder(pCodecCtx->codec_id); - if (pCodec == NULL) { - printf("Codec not found.\n"); - return -1; - } - if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { - printf("Could not open codec.\n"); - return -1; - } - AVFrame *pFrame, *pFrameYUV; - pFrame = av_frame_alloc(); - pFrameYUV = av_frame_alloc(); - // unsigned char *out_buffer=(unsigned char - // *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, - // pCodecCtx->height)); avpicture_fill((AVPicture *)pFrameYUV, out_buffer, - // AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); - // SDL---------------------------- - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { - printf("Could not initialize SDL - %s\n", SDL_GetError()); - return -1; - } - const int pixel_w = 640, pixel_h = 360; - int screen_w = 640, screen_h = 360; - // const SDL_VideoInfo *vi = SDL_GetVideoInfo(); - // Half of the Desktop's width and height. - screen_w = 640; - screen_h = 360; - // SDL_Surface *screen; - // screen = SDL_SetVideoMode(screen_w, screen_h, 0, 0); - SDL_Window *screen; - screen = SDL_CreateWindow("RTS Receiver", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h, - SDL_WINDOW_RESIZABLE); - - if (!screen) { - printf("SDL: could not set video mode - exiting:%s\n", SDL_GetError()); - return -1; - } - // SDL_Overlay *bmp; - // bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, - // SDL_YV12_OVERLAY, screen); - - sdlRenderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED); - - Uint32 pixformat = 0; - pixformat = SDL_PIXELFORMAT_NV12; - - SDL_Texture *sdlTexture = nullptr; - sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat, - SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h); - - SDL_Rect rect; - rect.x = 0; - rect.y = 0; - rect.w = screen_w; - rect.h = screen_h; - // SDL End------------------------ - int ret, got_picture; - - AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket)); - -#if OUTPUT_YUV420P - FILE *fp_yuv = fopen("output.yuv", "wb+"); -#endif - - struct SwsContext *img_convert_ctx; - img_convert_ctx = sws_getContext( - pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, - pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); - //------------------------------ - SDL_Thread *video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL); - // - // SDL_WM_SetCaption("Simplest FFmpeg Grab Desktop", NULL); - // Event Loop - SDL_Event event; - - for (;;) { - // Wait - SDL_WaitEvent(&event); - if (event.type == SFM_REFRESH_EVENT) { - //------------------------------ - if (av_read_frame(pFormatCtx, packet) >= 0) { - if (packet->stream_index == videoindex) { - avcodec_send_packet(pCodecCtx, packet); - got_picture = avcodec_receive_frame(pCodecCtx, pFrame); - // ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, - // packet); - if (ret < 0) { - printf("Decode Error.\n"); - return -1; - } - if (got_picture) { - // SDL_LockYUVOverlay(bmp); - // pFrameYUV->data[0] = bmp->pixels[0]; - // pFrameYUV->data[1] = bmp->pixels[2]; - // pFrameYUV->data[2] = bmp->pixels[1]; - // pFrameYUV->linesize[0] = bmp->pitches[0]; - // pFrameYUV->linesize[1] = bmp->pitches[2]; - // pFrameYUV->linesize[2] = bmp->pitches[1]; - // sws_scale(img_convert_ctx, - // (const unsigned char *const *)pFrame->data, - // pFrame->linesize, 0, pCodecCtx->height, - // pFrameYUV->data, pFrameYUV->linesize); - - // #if OUTPUT_YUV420P - // int y_size = pCodecCtx->width * pCodecCtx->height; - // fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv); // Y - // fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv); // - // U fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv); - // // V - // #endif - // SDL_UnlockYUVOverlay(bmp); - - // SDL_DisplayYUVOverlay(bmp, &rect); - - // YUV420ToNV12FFmpeg(buffer, pixel_w, pixel_h, dst_buffer); - - // SDL_UpdateTexture(sdlTexture, NULL, dst_buffer, pixel_w); - - // // FIX: If window is resize - // sdlRect.x = 0; - // sdlRect.y = 0; - // sdlRect.w = screen_w; - // sdlRect.h = screen_h; - - // SDL_RenderClear(sdlRenderer); - // SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect); - // SDL_RenderPresent(sdlRenderer); - } - } - // av_free_packet(packet); - } else { - // Exit Thread - thread_exit = 1; - } - } else if (event.type == SDL_QUIT) { - thread_exit = 1; - } else if (event.type == SFM_BREAK_EVENT) { - break; - } - } - - sws_freeContext(img_convert_ctx); - -#if OUTPUT_YUV420P - fclose(fp_yuv); -#endif - - SDL_Quit(); - - // av_free(out_buffer); - av_free(pFrameYUV); - avcodec_close(pCodecCtx); - avformat_close_input(&pFormatCtx); - - return 0; -} \ No newline at end of file diff --git a/application/remote_desk/remote_desk_server/main.cpp b/application/remote_desk/remote_desk_server/main.cpp new file mode 100644 index 0000000..c771b74 --- /dev/null +++ b/application/remote_desk/remote_desk_server/main.cpp @@ -0,0 +1,12 @@ +#include + +#include "remote_desk_server.h" + +int main() { + RemoteDeskServer remote_desk_server; + remote_desk_server.Init(); + + while (1) { + } + return 0; +} \ No newline at end of file diff --git a/application/remote_desk/remote_desk_server/remote_desk_server.cpp b/application/remote_desk/remote_desk_server/remote_desk_server.cpp new file mode 100644 index 0000000..1f2f03c --- /dev/null +++ b/application/remote_desk/remote_desk_server/remote_desk_server.cpp @@ -0,0 +1,93 @@ +#include "remote_desk_server.h" + +#include + +extern "C" { +#include +#include +#include +}; + +#define NV12_BUFFER_SIZE 2560 * 1440 * 3 / 2 + +RemoteDeskServer ::RemoteDeskServer() {} + +RemoteDeskServer ::~RemoteDeskServer() { + if (nv12_buffer_) { + delete nv12_buffer_; + nv12_buffer_ = nullptr; + } +} + +int BGRAToNV12FFmpeg(unsigned char *src_buffer, int width, int height, + unsigned char *dst_buffer) { + AVFrame *Input_pFrame = av_frame_alloc(); + AVFrame *Output_pFrame = av_frame_alloc(); + struct SwsContext *img_convert_ctx = sws_getContext( + width, height, AV_PIX_FMT_BGRA, width, height, AV_PIX_FMT_NV12, + SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); + + av_image_fill_arrays(Input_pFrame->data, Input_pFrame->linesize, src_buffer, + AV_PIX_FMT_BGRA, width, height, 1); + av_image_fill_arrays(Output_pFrame->data, Output_pFrame->linesize, dst_buffer, + AV_PIX_FMT_NV12, width, height, 1); + + sws_scale(img_convert_ctx, (uint8_t const **)Input_pFrame->data, + Input_pFrame->linesize, 0, height, Output_pFrame->data, + Output_pFrame->linesize); + + if (Input_pFrame) av_free(Input_pFrame); + if (Output_pFrame) av_free(Output_pFrame); + if (img_convert_ctx) sws_freeContext(img_convert_ctx); + + return 0; +} + +void RemoteDeskServer::HostReceiveBuffer(const char *data, size_t size, + const char *user_id, + size_t user_id_size) { + std::string msg(data, size); + std::string user(user_id, user_id_size); + + std::cout << "Receive: [" << user << "] " << msg << std::endl; +} + +int RemoteDeskServer::Init() { + Params params; + params.cfg_path = "../../../../config/config.ini"; + params.on_receive_buffer = [](const char *data, size_t size, + const char *user_id, size_t user_id_size) { + // std::string msg(data, size); + // std::string user(user_id, user_id_size); + + // std::cout << "Receive: [" << user << "] " << msg << std::endl; + }; + + std::string transmission_id = "000000"; + std::string user_id = "Server"; + peer = CreatePeer(¶ms); + CreateConnection(peer, transmission_id.c_str(), user_id.c_str()); + + nv12_buffer_ = new char[NV12_BUFFER_SIZE]; + + screen_capture = new ScreenCaptureWgc(); + + RECORD_DESKTOP_RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = GetSystemMetrics(SM_CXSCREEN); + rect.bottom = GetSystemMetrics(SM_CYSCREEN); + + screen_capture->Init( + rect, 60, + [this](unsigned char *data, int size, int width, int height) -> void { + // std::cout << "Send" << std::endl; + BGRAToNV12FFmpeg(data, width, height, (unsigned char *)nv12_buffer_); + SendData(peer, DATA_TYPE::VIDEO, (const char *)nv12_buffer_, + NV12_BUFFER_SIZE); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + }); + + screen_capture->Start(); + return 0; +} \ No newline at end of file diff --git a/application/remote_desk/remote_desk_server/remote_desk_server.h b/application/remote_desk/remote_desk_server/remote_desk_server.h new file mode 100644 index 0000000..8141e5d --- /dev/null +++ b/application/remote_desk/remote_desk_server/remote_desk_server.h @@ -0,0 +1,25 @@ +#ifndef _REMOTE_DESK_SERVER_H_ +#define _REMOTE_DESK_SERVER_H_ + +#include "screen_capture_wgc.h" +#include "x.h" + +class RemoteDeskServer { + public: + RemoteDeskServer(); + ~RemoteDeskServer(); + + public: + int Init(); + + static void HostReceiveBuffer(const char* data, size_t size, + const char* user_id, size_t user_id_size); + + private: + PeerPtr* peer = nullptr; + ScreenCaptureWgc* screen_capture = nullptr; + + char* nv12_buffer_ = nullptr; +}; + +#endif \ No newline at end of file diff --git a/application/remote_desk/dll/main.cpp b/application/remote_desk/screen_capture/main.cpp.bak similarity index 97% rename from application/remote_desk/dll/main.cpp rename to application/remote_desk/screen_capture/main.cpp.bak index f2a1f18..7c3d838 100644 --- a/application/remote_desk/dll/main.cpp +++ b/application/remote_desk/screen_capture/main.cpp.bak @@ -19,9 +19,7 @@ extern "C" { int screen_w = 2560, screen_h = 1440; const int pixel_w = 2560, pixel_h = 1440; -unsigned char buffer[pixel_w * pixel_h * 3 / 2]; unsigned char dst_buffer[pixel_w * pixel_h * 3 / 2]; -unsigned char rgbData[pixel_w * pixel_h * 4]; SDL_Texture *sdlTexture = nullptr; SDL_Renderer *sdlRenderer = nullptr; SDL_Rect sdlRect; diff --git a/application/remote_desk/dll/screen_capture_wgc.cpp b/application/remote_desk/screen_capture/screen_capture_wgc.cpp similarity index 54% rename from application/remote_desk/dll/screen_capture_wgc.cpp rename to application/remote_desk/screen_capture/screen_capture_wgc.cpp index 448a68c..f27f045 100644 --- a/application/remote_desk/dll/screen_capture_wgc.cpp +++ b/application/remote_desk/screen_capture/screen_capture_wgc.cpp @@ -55,10 +55,7 @@ int ScreenCaptureWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps, if (_inited == true) return error; _fps = fps; - _rect = rect; - _start_time = av_gettime_relative(); - _time_base = {1, AV_TIME_BASE}; - _pixel_fmt = AV_PIX_FMT_BGRA; + _on_data = cb; do { @@ -126,71 +123,9 @@ int ScreenCaptureWgc::Stop() { } void ScreenCaptureWgc::OnFrame(const WgcSession::wgc_session_frame &frame) { - // std::cout << "onframe" << std::endl; - // AVFrame *av_frame = av_frame_alloc(); - - // av_frame->pts = av_gettime_relative(); - // av_frame->pkt_dts = av_frame->pts; - // // av_frame->pkt_pts = av_frame->pts; - - // av_frame->width = frame.width; - // av_frame->height = frame.height; - // av_frame->format = AV_PIX_FMT_BGRA; - // av_frame->pict_type = AV_PICTURE_TYPE_NONE; - // av_frame->pkt_size = frame.width * frame.height * 4; - - // av_image_fill_arrays(av_frame->data, av_frame->linesize, frame.data, - // AV_PIX_FMT_BGRA, frame.width, frame.height, 1); - if (_on_data) _on_data((unsigned char *)frame.data, frame.width * frame.height * 4, frame.width, frame.height); - - // av_frame_free(&av_frame); - - // BGRA to YUV - // auto swrCtxBGRA2YUV = sws_getContext( - // frame.width, frame.height, AV_PIX_FMT_BGRA, frame.width, frame.height, - // AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); - - // create BGRA - // AVFrame *frame_bgra = av_frame; - // AVFrame *frame_bgra = av_frame_alloc(); - // frame_bgra->format = AV_PIX_FMT_BGRA; - // frame_bgra->width = frame.width; - // frame_bgra->height = frame.height; - // if (av_frame_get_buffer(frame_bgra, 32) < 0) { - // printf("Failed: av_frame_get_buffer\n"); - // return; - // } - // frame_bgra->data[0] = cropImage; - - // YUV - // AVFrame *frame_yuv = av_frame_alloc(); - // frame_yuv->width = frame.width; - // frame_yuv->height = frame.height; - // frame_yuv->format = AV_PIX_FMT_YUV420P; - - // uint8_t *picture_buf = - // (uint8_t *)av_malloc(frame.width * frame.height * 3 / 2); - // if (av_image_fill_arrays(frame_yuv->data, frame_yuv->linesize, picture_buf, - // AV_PIX_FMT_YUV420P, frame.width, frame.height, - // 1) < 0) { - // std::cout << "Failed: av_image_fill_arrays" << std::endl; - // return; - // } - - // if (sws_scale(swrCtxBGRA2YUV, frame_bgra->data, frame_bgra->linesize, 0, - // frame.height, frame_yuv->data, frame_yuv->linesize) < 0) { - // std::cout << "BGRA to YUV failed" << std::endl; - // return; - // } - - // frame_yuv->pts = av_gettime(); - - // if (_on_data) - // _on_data((unsigned char *)frame_yuv->data, - // frame.width * frame.height * 3 / 2, frame.width, frame.height); } void ScreenCaptureWgc::CleanUp() { diff --git a/application/remote_desk/dll/screen_capture_wgc.h b/application/remote_desk/screen_capture/screen_capture_wgc.h similarity index 79% rename from application/remote_desk/dll/screen_capture_wgc.h rename to application/remote_desk/screen_capture/screen_capture_wgc.h index 6a5ab86..5358234 100644 --- a/application/remote_desk/dll/screen_capture_wgc.h +++ b/application/remote_desk/screen_capture/screen_capture_wgc.h @@ -3,23 +3,14 @@ #include -#include "wgc_session.h" -#include "wgc_session_impl.h" - -extern "C" { -#include -#include -#include -#include -#include -#include -} - #include #include #include #include +#include "wgc_session.h" +#include "wgc_session_impl.h" + typedef struct { int left; int top; @@ -67,10 +58,6 @@ class ScreenCaptureWgc : public WgcSession::wgc_session_observer { cb_desktop_data _on_data; cb_desktop_error _on_error; - - AVRational _time_base; - int64_t _start_time; - AVPixelFormat _pixel_fmt; }; #endif \ No newline at end of file diff --git a/application/remote_desk/dll/wgc_session.h b/application/remote_desk/screen_capture/wgc_session.h similarity index 100% rename from application/remote_desk/dll/wgc_session.h rename to application/remote_desk/screen_capture/wgc_session.h diff --git a/application/remote_desk/dll/wgc_session_impl.cpp b/application/remote_desk/screen_capture/wgc_session_impl.cpp similarity index 100% rename from application/remote_desk/dll/wgc_session_impl.cpp rename to application/remote_desk/screen_capture/wgc_session_impl.cpp diff --git a/application/remote_desk/dll/wgc_session_impl.h b/application/remote_desk/screen_capture/wgc_session_impl.h similarity index 100% rename from application/remote_desk/dll/wgc_session_impl.h rename to application/remote_desk/screen_capture/wgc_session_impl.h diff --git a/application/remote_desk/webrtc/wgc_capture_session.cc b/application/remote_desk/webrtc/wgc_capture_session.cc deleted file mode 100644 index a577a84..0000000 --- a/application/remote_desk/webrtc/wgc_capture_session.cc +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/desktop_capture/win/wgc_capture_session.h" - -#include -#include -#include - -#include -#include -#include - -#include "modules/desktop_capture/win/wgc_desktop_frame.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" -#include "rtc_base/time_utils.h" -#include "rtc_base/win/create_direct3d_device.h" -#include "rtc_base/win/get_activation_factory.h" -#include "system_wrappers/include/metrics.h" - -using Microsoft::WRL::ComPtr; -namespace WGC = ABI::Windows::Graphics::Capture; - -namespace webrtc { -namespace { - -// We must use a BGRA pixel format that has 4 bytes per pixel, as required by -// the DesktopFrame interface. -const auto kPixelFormat = ABI::Windows::Graphics::DirectX::DirectXPixelFormat:: - DirectXPixelFormat_B8G8R8A8UIntNormalized; - -// We only want 1 buffer in our frame pool to reduce latency. If we had more, -// they would sit in the pool for longer and be stale by the time we are asked -// for a new frame. -const int kNumBuffers = 1; - -// These values are persisted to logs. Entries should not be renumbered and -// numeric values should never be reused. -enum class StartCaptureResult { - kSuccess = 0, - kSourceClosed = 1, - kAddClosedFailed = 2, - kDxgiDeviceCastFailed = 3, - kD3dDelayLoadFailed = 4, - kD3dDeviceCreationFailed = 5, - kFramePoolActivationFailed = 6, - kFramePoolCastFailed = 7, - kGetItemSizeFailed = 8, - kCreateFreeThreadedFailed = 9, - kCreateCaptureSessionFailed = 10, - kStartCaptureFailed = 11, - kMaxValue = kStartCaptureFailed -}; - -// These values are persisted to logs. Entries should not be renumbered and -// numeric values should never be reused. -enum class GetFrameResult { - kSuccess = 0, - kItemClosed = 1, - kTryGetNextFrameFailed = 2, - kFrameDropped = 3, - kGetSurfaceFailed = 4, - kDxgiInterfaceAccessFailed = 5, - kTexture2dCastFailed = 6, - kCreateMappedTextureFailed = 7, - kMapFrameFailed = 8, - kGetContentSizeFailed = 9, - kResizeMappedTextureFailed = 10, - kRecreateFramePoolFailed = 11, - kMaxValue = kRecreateFramePoolFailed -}; - -void RecordStartCaptureResult(StartCaptureResult error) { - RTC_HISTOGRAM_ENUMERATION( - "WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult", - static_cast(error), static_cast(StartCaptureResult::kMaxValue)); -} - -void RecordGetFrameResult(GetFrameResult error) { - RTC_HISTOGRAM_ENUMERATION( - "WebRTC.DesktopCapture.Win.WgcCaptureSessionGetFrameResult", - static_cast(error), static_cast(GetFrameResult::kMaxValue)); -} - -} // namespace - -WgcCaptureSession::WgcCaptureSession(ComPtr d3d11_device, - ComPtr item) - : d3d11_device_(std::move(d3d11_device)), item_(std::move(item)) {} -WgcCaptureSession::~WgcCaptureSession() = default; - -HRESULT WgcCaptureSession::StartCapture() { - RTC_DCHECK_RUN_ON(&sequence_checker_); - RTC_DCHECK(!is_capture_started_); - - if (item_closed_) { - RTC_LOG(LS_ERROR) << "The target source has been closed."; - RecordStartCaptureResult(StartCaptureResult::kSourceClosed); - return E_ABORT; - } - - RTC_DCHECK(d3d11_device_); - RTC_DCHECK(item_); - - // Listen for the Closed event, to detect if the source we are capturing is - // closed (e.g. application window is closed or monitor is disconnected). If - // it is, we should abort the capture. - auto closed_handler = - Microsoft::WRL::Callback>( - this, &WgcCaptureSession::OnItemClosed); - EventRegistrationToken item_closed_token; - HRESULT hr = item_->add_Closed(closed_handler.Get(), &item_closed_token); - if (FAILED(hr)) { - RecordStartCaptureResult(StartCaptureResult::kAddClosedFailed); - return hr; - } - - ComPtr dxgi_device; - hr = d3d11_device_->QueryInterface(IID_PPV_ARGS(&dxgi_device)); - if (FAILED(hr)) { - RecordStartCaptureResult(StartCaptureResult::kDxgiDeviceCastFailed); - return hr; - } - - if (!ResolveCoreWinRTDirect3DDelayload()) { - RecordStartCaptureResult(StartCaptureResult::kD3dDelayLoadFailed); - return E_FAIL; - } - - hr = CreateDirect3DDeviceFromDXGIDevice(dxgi_device.Get(), &direct3d_device_); - if (FAILED(hr)) { - RecordStartCaptureResult(StartCaptureResult::kD3dDeviceCreationFailed); - return hr; - } - - ComPtr frame_pool_statics; - hr = GetActivationFactory< - ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePoolStatics, - RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool>( - &frame_pool_statics); - if (FAILED(hr)) { - RecordStartCaptureResult(StartCaptureResult::kFramePoolActivationFailed); - return hr; - } - - // Cast to FramePoolStatics2 so we can use CreateFreeThreaded and avoid the - // need to have a DispatcherQueue. We don't listen for the FrameArrived event, - // so there's no difference. - ComPtr frame_pool_statics2; - hr = frame_pool_statics->QueryInterface(IID_PPV_ARGS(&frame_pool_statics2)); - if (FAILED(hr)) { - RecordStartCaptureResult(StartCaptureResult::kFramePoolCastFailed); - return hr; - } - - ABI::Windows::Graphics::SizeInt32 item_size; - hr = item_.Get()->get_Size(&item_size); - if (FAILED(hr)) { - RecordStartCaptureResult(StartCaptureResult::kGetItemSizeFailed); - return hr; - } - - previous_size_ = item_size; - - hr = frame_pool_statics2->CreateFreeThreaded(direct3d_device_.Get(), - kPixelFormat, kNumBuffers, - item_size, &frame_pool_); - if (FAILED(hr)) { - RecordStartCaptureResult(StartCaptureResult::kCreateFreeThreadedFailed); - return hr; - } - - hr = frame_pool_->CreateCaptureSession(item_.Get(), &session_); - if (FAILED(hr)) { - RecordStartCaptureResult(StartCaptureResult::kCreateCaptureSessionFailed); - return hr; - } - - hr = session_->StartCapture(); - if (FAILED(hr)) { - RTC_LOG(LS_ERROR) << "Failed to start CaptureSession: " << hr; - RecordStartCaptureResult(StartCaptureResult::kStartCaptureFailed); - return hr; - } - - RecordStartCaptureResult(StartCaptureResult::kSuccess); - - is_capture_started_ = true; - return hr; -} - -HRESULT WgcCaptureSession::GetFrame( - std::unique_ptr* output_frame) { - RTC_DCHECK_RUN_ON(&sequence_checker_); - - if (item_closed_) { - RTC_LOG(LS_ERROR) << "The target source has been closed."; - RecordGetFrameResult(GetFrameResult::kItemClosed); - return E_ABORT; - } - - RTC_DCHECK(is_capture_started_); - - ComPtr capture_frame; - HRESULT hr = frame_pool_->TryGetNextFrame(&capture_frame); - if (FAILED(hr)) { - RTC_LOG(LS_ERROR) << "TryGetNextFrame failed: " << hr; - RecordGetFrameResult(GetFrameResult::kTryGetNextFrameFailed); - return hr; - } - - if (!capture_frame) { - RecordGetFrameResult(GetFrameResult::kFrameDropped); - return hr; - } - - // We need to get this CaptureFrame as an ID3D11Texture2D so that we can get - // the raw image data in the format required by the DesktopFrame interface. - ComPtr - d3d_surface; - hr = capture_frame->get_Surface(&d3d_surface); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kGetSurfaceFailed); - return hr; - } - - ComPtr - direct3DDxgiInterfaceAccess; - hr = d3d_surface->QueryInterface(IID_PPV_ARGS(&direct3DDxgiInterfaceAccess)); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kDxgiInterfaceAccessFailed); - return hr; - } - - ComPtr texture_2D; - hr = direct3DDxgiInterfaceAccess->GetInterface(IID_PPV_ARGS(&texture_2D)); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kTexture2dCastFailed); - return hr; - } - - if (!mapped_texture_) { - hr = CreateMappedTexture(texture_2D); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kCreateMappedTextureFailed); - return hr; - } - } - - // We need to copy |texture_2D| into |mapped_texture_| as the latter has the - // D3D11_CPU_ACCESS_READ flag set, which lets us access the image data. - // Otherwise it would only be readable by the GPU. - ComPtr d3d_context; - d3d11_device_->GetImmediateContext(&d3d_context); - d3d_context->CopyResource(mapped_texture_.Get(), texture_2D.Get()); - - D3D11_MAPPED_SUBRESOURCE map_info; - hr = d3d_context->Map(mapped_texture_.Get(), /*subresource_index=*/0, - D3D11_MAP_READ, /*D3D11_MAP_FLAG_DO_NOT_WAIT=*/0, - &map_info); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kMapFrameFailed); - return hr; - } - - ABI::Windows::Graphics::SizeInt32 new_size; - hr = capture_frame->get_ContentSize(&new_size); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kGetContentSizeFailed); - return hr; - } - - // If the size has changed since the last capture, we must be sure to use - // the smaller dimensions. Otherwise we might overrun our buffer, or - // read stale data from the last frame. - int image_height = std::min(previous_size_.Height, new_size.Height); - int image_width = std::min(previous_size_.Width, new_size.Width); - int row_data_length = image_width * DesktopFrame::kBytesPerPixel; - - // Make a copy of the data pointed to by |map_info.pData| so we are free to - // unmap our texture. - uint8_t* src_data = static_cast(map_info.pData); - std::vector image_data; - image_data.reserve(image_height * row_data_length); - uint8_t* image_data_ptr = image_data.data(); - for (int i = 0; i < image_height; i++) { - memcpy(image_data_ptr, src_data, row_data_length); - image_data_ptr += row_data_length; - src_data += map_info.RowPitch; - } - - // Transfer ownership of |image_data| to the output_frame. - DesktopSize size(image_width, image_height); - *output_frame = std::make_unique(size, row_data_length, - std::move(image_data)); - - d3d_context->Unmap(mapped_texture_.Get(), 0); - - // If the size changed, we must resize the texture and frame pool to fit the - // new size. - if (previous_size_.Height != new_size.Height || - previous_size_.Width != new_size.Width) { - hr = CreateMappedTexture(texture_2D, new_size.Width, new_size.Height); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kResizeMappedTextureFailed); - return hr; - } - - hr = frame_pool_->Recreate(direct3d_device_.Get(), kPixelFormat, - kNumBuffers, new_size); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kRecreateFramePoolFailed); - return hr; - } - } - - RecordGetFrameResult(GetFrameResult::kSuccess); - - previous_size_ = new_size; - return hr; -} - -HRESULT WgcCaptureSession::CreateMappedTexture( - ComPtr src_texture, - UINT width, - UINT height) { - RTC_DCHECK_RUN_ON(&sequence_checker_); - - D3D11_TEXTURE2D_DESC src_desc; - src_texture->GetDesc(&src_desc); - D3D11_TEXTURE2D_DESC map_desc; - map_desc.Width = width == 0 ? src_desc.Width : width; - map_desc.Height = height == 0 ? src_desc.Height : height; - map_desc.MipLevels = src_desc.MipLevels; - map_desc.ArraySize = src_desc.ArraySize; - map_desc.Format = src_desc.Format; - map_desc.SampleDesc = src_desc.SampleDesc; - map_desc.Usage = D3D11_USAGE_STAGING; - map_desc.BindFlags = 0; - map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - map_desc.MiscFlags = 0; - return d3d11_device_->CreateTexture2D(&map_desc, nullptr, &mapped_texture_); -} - -HRESULT WgcCaptureSession::OnItemClosed(WGC::IGraphicsCaptureItem* sender, - IInspectable* event_args) { - RTC_DCHECK_RUN_ON(&sequence_checker_); - - RTC_LOG(LS_INFO) << "Capture target has been closed."; - item_closed_ = true; - is_capture_started_ = false; - - mapped_texture_ = nullptr; - session_ = nullptr; - frame_pool_ = nullptr; - direct3d_device_ = nullptr; - item_ = nullptr; - d3d11_device_ = nullptr; - - return S_OK; -} - -} // namespace webrtc diff --git a/application/remote_desk/webrtc/wgc_capture_session.h b/application/remote_desk/webrtc/wgc_capture_session.h deleted file mode 100644 index a392971..0000000 --- a/application/remote_desk/webrtc/wgc_capture_session.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_ -#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_ - -#include -#include -#include - -#include - -#include "api/sequence_checker.h" -#include "modules/desktop_capture/desktop_capture_options.h" -#include "modules/desktop_capture/win/wgc_capture_source.h" - -namespace webrtc { - -class WgcCaptureSession final { - public: - WgcCaptureSession( - Microsoft::WRL::ComPtr d3d11_device, - Microsoft::WRL::ComPtr< - ABI::Windows::Graphics::Capture::IGraphicsCaptureItem> item); - - // Disallow copy and assign. - WgcCaptureSession(const WgcCaptureSession&) = delete; - WgcCaptureSession& operator=(const WgcCaptureSession&) = delete; - - ~WgcCaptureSession(); - - HRESULT StartCapture(); - - // Returns a frame from the frame pool, if any are present. - HRESULT GetFrame(std::unique_ptr* output_frame); - - bool IsCaptureStarted() const { - RTC_DCHECK_RUN_ON(&sequence_checker_); - return is_capture_started_; - } - - private: - // Initializes |mapped_texture_| with the properties of the |src_texture|, - // overrides the values of some necessary properties like the - // D3D11_CPU_ACCESS_READ flag. Also has optional parameters for what size - // |mapped_texture_| should be, if they aren't provided we will use the size - // of |src_texture|. - HRESULT CreateMappedTexture( - Microsoft::WRL::ComPtr src_texture, - UINT width = 0, - UINT height = 0); - - // Event handler for |item_|'s Closed event. - HRESULT OnItemClosed( - ABI::Windows::Graphics::Capture::IGraphicsCaptureItem* sender, - IInspectable* event_args); - - // A Direct3D11 Device provided by the caller. We use this to create an - // IDirect3DDevice, and also to create textures that will hold the image data. - Microsoft::WRL::ComPtr d3d11_device_; - - // This item represents what we are capturing, we use it to create the - // capture session, and also to listen for the Closed event. - Microsoft::WRL::ComPtr - item_; - - // The IDirect3DDevice is necessary to instantiate the frame pool. - Microsoft::WRL::ComPtr< - ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice> - direct3d_device_; - - // The frame pool is where frames are deposited during capture, we retrieve - // them from here with TryGetNextFrame(). - Microsoft::WRL::ComPtr< - ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool> - frame_pool_; - - // This texture holds the final image data. We made it a member so we can - // reuse it, instead of having to create a new texture every time we grab a - // frame. - Microsoft::WRL::ComPtr mapped_texture_; - - // This lets us know when the source has been resized, which is important - // because we must resize the framepool and our texture to be able to hold - // enough data for the frame. - ABI::Windows::Graphics::SizeInt32 previous_size_; - - // The capture session lets us set properties about the capture before it - // starts such as whether to capture the mouse cursor, and it lets us tell WGC - // to start capturing frames. - Microsoft::WRL::ComPtr< - ABI::Windows::Graphics::Capture::IGraphicsCaptureSession> - session_; - - bool item_closed_ = false; - bool is_capture_started_ = false; - - SequenceChecker sequence_checker_; -}; - -} // namespace webrtc - -#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_ diff --git a/application/remote_desk/webrtc/wgc_capture_source.cc b/application/remote_desk/webrtc/wgc_capture_source.cc deleted file mode 100644 index 0532bd4..0000000 --- a/application/remote_desk/webrtc/wgc_capture_source.cc +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include "modules/desktop_capture/win/wgc_capture_source.h" -#include -#include -#include -#include "modules/desktop_capture/win/screen_capture_utils.h" -#include "modules/desktop_capture/win/window_capture_utils.h" -#include "rtc_base/win/get_activation_factory.h" -using Microsoft::WRL::ComPtr; -namespace WGC = ABI::Windows::Graphics::Capture; -namespace webrtc { -WgcCaptureSource::WgcCaptureSource(DesktopCapturer::SourceId source_id) - : source_id_(source_id) {} -WgcCaptureSource::~WgcCaptureSource() = default; -bool WgcCaptureSource::IsCapturable() { - // If we can create a capture item, then we can capture it. Unfortunately, - // we can't cache this item because it may be created in a different COM - // apartment than where capture will eventually start from. - ComPtr item; - return SUCCEEDED(CreateCaptureItem(&item)); -} -bool WgcCaptureSource::FocusOnSource() { - return false; -} -HRESULT WgcCaptureSource::GetCaptureItem( - ComPtr* result) { - HRESULT hr = S_OK; - if (!item_) - hr = CreateCaptureItem(&item_); - *result = item_; - return hr; -} -WgcCaptureSourceFactory::~WgcCaptureSourceFactory() = default; -WgcWindowSourceFactory::WgcWindowSourceFactory() = default; -WgcWindowSourceFactory::~WgcWindowSourceFactory() = default; -std::unique_ptr WgcWindowSourceFactory::CreateCaptureSource( - DesktopCapturer::SourceId source_id) { - return std::make_unique(source_id); -} -WgcScreenSourceFactory::WgcScreenSourceFactory() = default; -WgcScreenSourceFactory::~WgcScreenSourceFactory() = default; -std::unique_ptr WgcScreenSourceFactory::CreateCaptureSource( - DesktopCapturer::SourceId source_id) { - return std::make_unique(source_id); -} -WgcWindowSource::WgcWindowSource(DesktopCapturer::SourceId source_id) - : WgcCaptureSource(source_id) {} -WgcWindowSource::~WgcWindowSource() = default; -DesktopVector WgcWindowSource::GetTopLeft() { - DesktopRect window_rect; - if (!GetWindowRect(reinterpret_cast(GetSourceId()), &window_rect)) - return DesktopVector(); - return window_rect.top_left(); -} -bool WgcWindowSource::IsCapturable() { - if (!IsWindowValidAndVisible(reinterpret_cast(GetSourceId()))) - return false; - return WgcCaptureSource::IsCapturable(); -} -bool WgcWindowSource::FocusOnSource() { - if (!IsWindowValidAndVisible(reinterpret_cast(GetSourceId()))) - return false; - return ::BringWindowToTop(reinterpret_cast(GetSourceId())) && - ::SetForegroundWindow(reinterpret_cast(GetSourceId())); -} -HRESULT WgcWindowSource::CreateCaptureItem( - ComPtr* result) { - if (!ResolveCoreWinRTDelayload()) - return E_FAIL; - ComPtr interop; - HRESULT hr = GetActivationFactory< - IGraphicsCaptureItemInterop, - RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop); - if (FAILED(hr)) - return hr; - ComPtr item; - hr = interop->CreateForWindow(reinterpret_cast(GetSourceId()), - IID_PPV_ARGS(&item)); - if (FAILED(hr)) - return hr; - if (!item) - return E_HANDLE; - *result = std::move(item); - return hr; -} -WgcScreenSource::WgcScreenSource(DesktopCapturer::SourceId source_id) - : WgcCaptureSource(source_id) { - // Getting the HMONITOR could fail if the source_id is invalid. In that case, - // we leave hmonitor_ uninitialized and |IsCapturable()| will fail. - HMONITOR hmon; - if (GetHmonitorFromDeviceIndex(GetSourceId(), &hmon)) - hmonitor_ = hmon; -} -WgcScreenSource::~WgcScreenSource() = default; -DesktopVector WgcScreenSource::GetTopLeft() { - if (!hmonitor_) - return DesktopVector(); - return GetMonitorRect(*hmonitor_).top_left(); -} -bool WgcScreenSource::IsCapturable() { - if (!hmonitor_) - return false; - if (!IsMonitorValid(*hmonitor_)) - return false; - return WgcCaptureSource::IsCapturable(); -} -HRESULT WgcScreenSource::CreateCaptureItem( - ComPtr* result) { - if (!hmonitor_) - return E_ABORT; - if (!ResolveCoreWinRTDelayload()) - return E_FAIL; - ComPtr interop; - HRESULT hr = GetActivationFactory< - IGraphicsCaptureItemInterop, - RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop); - if (FAILED(hr)) - return hr; - ComPtr item; - hr = interop->CreateForMonitor(*hmonitor_, IID_PPV_ARGS(&item)); - if (FAILED(hr)) - return hr; - if (!item) - return E_HANDLE; - *result = std::move(item); - return hr; -} -} // namespace webrtc diff --git a/application/remote_desk/webrtc/wgc_capture_source.h b/application/remote_desk/webrtc/wgc_capture_source.h deleted file mode 100644 index 04e4519..0000000 --- a/application/remote_desk/webrtc/wgc_capture_source.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#ifndef MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_ -#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_ -#include -#include -#include -#include "absl/types/optional.h" -#include "modules/desktop_capture/desktop_capturer.h" -#include "modules/desktop_capture/desktop_geometry.h" -namespace webrtc { -// Abstract class to represent the source that WGC-based capturers capture -// from. Could represent an application window or a screen. Consumers should use -// the appropriate Wgc*SourceFactory class to create WgcCaptureSource objects -// of the appropriate type. -class WgcCaptureSource { - public: - explicit WgcCaptureSource(DesktopCapturer::SourceId source_id); - virtual ~WgcCaptureSource(); - virtual DesktopVector GetTopLeft() = 0; - virtual bool IsCapturable(); - virtual bool FocusOnSource(); - HRESULT GetCaptureItem( - Microsoft::WRL::ComPtr< - ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result); - DesktopCapturer::SourceId GetSourceId() { return source_id_; } - protected: - virtual HRESULT CreateCaptureItem( - Microsoft::WRL::ComPtr< - ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result) = 0; - private: - Microsoft::WRL::ComPtr - item_; - const DesktopCapturer::SourceId source_id_; -}; -class WgcCaptureSourceFactory { - public: - virtual ~WgcCaptureSourceFactory(); - virtual std::unique_ptr CreateCaptureSource( - DesktopCapturer::SourceId) = 0; -}; -class WgcWindowSourceFactory final : public WgcCaptureSourceFactory { - public: - WgcWindowSourceFactory(); - // Disallow copy and assign. - WgcWindowSourceFactory(const WgcWindowSourceFactory&) = delete; - WgcWindowSourceFactory& operator=(const WgcWindowSourceFactory&) = delete; - ~WgcWindowSourceFactory() override; - std::unique_ptr CreateCaptureSource( - DesktopCapturer::SourceId) override; -}; -class WgcScreenSourceFactory final : public WgcCaptureSourceFactory { - public: - WgcScreenSourceFactory(); - WgcScreenSourceFactory(const WgcScreenSourceFactory&) = delete; - WgcScreenSourceFactory& operator=(const WgcScreenSourceFactory&) = delete; - ~WgcScreenSourceFactory() override; - std::unique_ptr CreateCaptureSource( - DesktopCapturer::SourceId) override; -}; -// Class for capturing application windows. -class WgcWindowSource final : public WgcCaptureSource { - public: - explicit WgcWindowSource(DesktopCapturer::SourceId source_id); - WgcWindowSource(const WgcWindowSource&) = delete; - WgcWindowSource& operator=(const WgcWindowSource&) = delete; - ~WgcWindowSource() override; - DesktopVector GetTopLeft() override; - bool IsCapturable() override; - bool FocusOnSource() override; - private: - HRESULT CreateCaptureItem( - Microsoft::WRL::ComPtr< - ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result) - override; -}; -// Class for capturing screens/monitors/displays. -class WgcScreenSource final : public WgcCaptureSource { - public: - explicit WgcScreenSource(DesktopCapturer::SourceId source_id); - WgcScreenSource(const WgcScreenSource&) = delete; - WgcScreenSource& operator=(const WgcScreenSource&) = delete; - ~WgcScreenSource() override; - DesktopVector GetTopLeft() override; - bool IsCapturable() override; - private: - HRESULT CreateCaptureItem( - Microsoft::WRL::ComPtr< - ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result) - override; - // To maintain compatibility with other capturers, this class accepts a - // device index as it's SourceId. However, WGC requires we use an HMONITOR to - // describe which screen to capture. So, we internally convert the supplied - // device index into an HMONITOR when |IsCapturable()| is called. - absl::optional hmonitor_; -}; -} // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_ diff --git a/application/remote_desk/webrtc/wgc_capture_source_unittest.cc b/application/remote_desk/webrtc/wgc_capture_source_unittest.cc deleted file mode 100644 index fffcbfe..0000000 --- a/application/remote_desk/webrtc/wgc_capture_source_unittest.cc +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include "modules/desktop_capture/win/wgc_capture_source.h" -#include -#include -#include -#include "modules/desktop_capture/desktop_capture_types.h" -#include "modules/desktop_capture/desktop_geometry.h" -#include "modules/desktop_capture/win/screen_capture_utils.h" -#include "modules/desktop_capture/win/test_support/test_window.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" -#include "rtc_base/win/scoped_com_initializer.h" -#include "rtc_base/win/windows_version.h" -#include "test/gtest.h" -namespace webrtc { -namespace { -const WCHAR kWindowTitle[] = L"WGC Capture Source Test Window"; -const int kFirstXCoord = 25; -const int kFirstYCoord = 50; -const int kSecondXCoord = 50; -const int kSecondYCoord = 75; -enum SourceType { kWindowSource = 0, kScreenSource = 1 }; -} // namespace -class WgcCaptureSourceTest : public ::testing::TestWithParam { - public: - void SetUp() override { - if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_RS5) { - RTC_LOG(LS_INFO) - << "Skipping WgcCaptureSourceTests on Windows versions < RS5."; - GTEST_SKIP(); - } - com_initializer_ = - std::make_unique(ScopedCOMInitializer::kMTA); - ASSERT_TRUE(com_initializer_->Succeeded()); - } - void TearDown() override { - if (window_open_) { - DestroyTestWindow(window_info_); - } - } - void SetUpForWindowSource() { - window_info_ = CreateTestWindow(kWindowTitle); - window_open_ = true; - source_id_ = reinterpret_cast(window_info_.hwnd); - source_factory_ = std::make_unique(); - } - void SetUpForScreenSource() { - source_id_ = kFullDesktopScreenId; - source_factory_ = std::make_unique(); - } - protected: - std::unique_ptr com_initializer_; - std::unique_ptr source_factory_; - std::unique_ptr source_; - DesktopCapturer::SourceId source_id_; - WindowInfo window_info_; - bool window_open_ = false; -}; -// Window specific test -TEST_F(WgcCaptureSourceTest, WindowPosition) { - SetUpForWindowSource(); - source_ = source_factory_->CreateCaptureSource(source_id_); - ASSERT_TRUE(source_); - EXPECT_EQ(source_->GetSourceId(), source_id_); - MoveTestWindow(window_info_.hwnd, kFirstXCoord, kFirstYCoord); - DesktopVector source_vector = source_->GetTopLeft(); - EXPECT_EQ(source_vector.x(), kFirstXCoord); - EXPECT_EQ(source_vector.y(), kFirstYCoord); - MoveTestWindow(window_info_.hwnd, kSecondXCoord, kSecondYCoord); - source_vector = source_->GetTopLeft(); - EXPECT_EQ(source_vector.x(), kSecondXCoord); - EXPECT_EQ(source_vector.y(), kSecondYCoord); -} -// Screen specific test -TEST_F(WgcCaptureSourceTest, ScreenPosition) { - SetUpForScreenSource(); - source_ = source_factory_->CreateCaptureSource(source_id_); - ASSERT_TRUE(source_); - EXPECT_EQ(source_id_, source_->GetSourceId()); - DesktopRect screen_rect = GetFullscreenRect(); - DesktopVector source_vector = source_->GetTopLeft(); - EXPECT_EQ(source_vector.x(), screen_rect.left()); - EXPECT_EQ(source_vector.y(), screen_rect.top()); -} -// Source agnostic test -TEST_P(WgcCaptureSourceTest, CreateSource) { - if (GetParam() == SourceType::kWindowSource) { - SetUpForWindowSource(); - } else { - SetUpForScreenSource(); - } - source_ = source_factory_->CreateCaptureSource(source_id_); - ASSERT_TRUE(source_); - EXPECT_EQ(source_id_, source_->GetSourceId()); - EXPECT_TRUE(source_->IsCapturable()); - Microsoft::WRL::ComPtr - item; - EXPECT_TRUE(SUCCEEDED(source_->GetCaptureItem(&item))); - EXPECT_TRUE(item); -} -INSTANTIATE_TEST_SUITE_P(SourceAgnostic, - WgcCaptureSourceTest, - ::testing::Values(SourceType::kWindowSource, - SourceType::kScreenSource)); -} // namespace webrtc diff --git a/application/remote_desk/webrtc/wgc_capturer_win.cc b/application/remote_desk/webrtc/wgc_capturer_win.cc deleted file mode 100644 index 9b2cce4..0000000 --- a/application/remote_desk/webrtc/wgc_capturer_win.cc +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/desktop_capture/win/wgc_capturer_win.h" - -#include - -#include "modules/desktop_capture/desktop_capture_metrics_helper.h" -#include "modules/desktop_capture/desktop_capture_types.h" -#include "modules/desktop_capture/win/wgc_desktop_frame.h" -#include "rtc_base/logging.h" -#include "rtc_base/time_utils.h" -#include "system_wrappers/include/metrics.h" - -namespace WGC = ABI::Windows::Graphics::Capture; -using Microsoft::WRL::ComPtr; - -namespace webrtc { - -namespace { - -enum class WgcCapturerResult { - kSuccess = 0, - kNoDirect3dDevice = 1, - kNoSourceSelected = 2, - kItemCreationFailure = 3, - kSessionStartFailure = 4, - kGetFrameFailure = 5, - kFrameDropped = 6, - kMaxValue = kFrameDropped -}; - -void RecordWgcCapturerResult(WgcCapturerResult error) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.DesktopCapture.Win.WgcCapturerResult", - static_cast(error), - static_cast(WgcCapturerResult::kMaxValue)); -} - -} // namespace - -WgcCapturerWin::WgcCapturerWin( - std::unique_ptr source_factory, - std::unique_ptr source_enumerator) - : source_factory_(std::move(source_factory)), - source_enumerator_(std::move(source_enumerator)) {} -WgcCapturerWin::~WgcCapturerWin() = default; - -// static -std::unique_ptr WgcCapturerWin::CreateRawWindowCapturer( - const DesktopCaptureOptions& options) { - return std::make_unique( - std::make_unique(), - std::make_unique( - options.enumerate_current_process_windows())); -} - -// static -std::unique_ptr WgcCapturerWin::CreateRawScreenCapturer( - const DesktopCaptureOptions& options) { - return std::make_unique( - std::make_unique(), - std::make_unique()); -} - -bool WgcCapturerWin::GetSourceList(SourceList* sources) { - return source_enumerator_->FindAllSources(sources); -} - -bool WgcCapturerWin::SelectSource(DesktopCapturer::SourceId id) { - capture_source_ = source_factory_->CreateCaptureSource(id); - return capture_source_->IsCapturable(); -} - -bool WgcCapturerWin::FocusOnSelectedSource() { - if (!capture_source_) - return false; - - return capture_source_->FocusOnSource(); -} - -void WgcCapturerWin::Start(Callback* callback) { - RTC_DCHECK(!callback_); - RTC_DCHECK(callback); - RecordCapturerImpl(DesktopCapturerId::kWgcCapturerWin); - - callback_ = callback; - - // Create a Direct3D11 device to share amongst the WgcCaptureSessions. Many - // parameters are nullptr as the implemention uses defaults that work well for - // us. - HRESULT hr = D3D11CreateDevice( - /*adapter=*/nullptr, D3D_DRIVER_TYPE_HARDWARE, - /*software_rasterizer=*/nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, - /*feature_levels=*/nullptr, /*feature_levels_size=*/0, D3D11_SDK_VERSION, - &d3d11_device_, /*feature_level=*/nullptr, /*device_context=*/nullptr); - if (hr == DXGI_ERROR_UNSUPPORTED) { - // If a hardware device could not be created, use WARP which is a high speed - // software device. - hr = D3D11CreateDevice( - /*adapter=*/nullptr, D3D_DRIVER_TYPE_WARP, - /*software_rasterizer=*/nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, - /*feature_levels=*/nullptr, /*feature_levels_size=*/0, - D3D11_SDK_VERSION, &d3d11_device_, /*feature_level=*/nullptr, - /*device_context=*/nullptr); - } - - if (FAILED(hr)) { - RTC_LOG(LS_ERROR) << "Failed to create D3D11Device: " << hr; - } -} - -void WgcCapturerWin::CaptureFrame() { - RTC_DCHECK(callback_); - - if (!capture_source_) { - RTC_LOG(LS_ERROR) << "Source hasn't been selected"; - callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, - /*frame=*/nullptr); - RecordWgcCapturerResult(WgcCapturerResult::kNoSourceSelected); - return; - } - - if (!d3d11_device_) { - RTC_LOG(LS_ERROR) << "No D3D11D3evice, cannot capture."; - callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, - /*frame=*/nullptr); - RecordWgcCapturerResult(WgcCapturerResult::kNoDirect3dDevice); - return; - } - - int64_t capture_start_time_nanos = rtc::TimeNanos(); - - HRESULT hr; - WgcCaptureSession* capture_session = nullptr; - std::map::iterator session_iter = - ongoing_captures_.find(capture_source_->GetSourceId()); - if (session_iter == ongoing_captures_.end()) { - ComPtr item; - hr = capture_source_->GetCaptureItem(&item); - if (FAILED(hr)) { - RTC_LOG(LS_ERROR) << "Failed to create a GraphicsCaptureItem: " << hr; - callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, - /*frame=*/nullptr); - RecordWgcCapturerResult(WgcCapturerResult::kItemCreationFailure); - return; - } - - std::pair::iterator, bool> - iter_success_pair = ongoing_captures_.emplace( - std::piecewise_construct, - std::forward_as_tuple(capture_source_->GetSourceId()), - std::forward_as_tuple(d3d11_device_, item)); - RTC_DCHECK(iter_success_pair.second); - capture_session = &iter_success_pair.first->second; - } else { - capture_session = &session_iter->second; - } - - if (!capture_session->IsCaptureStarted()) { - hr = capture_session->StartCapture(); - if (FAILED(hr)) { - RTC_LOG(LS_ERROR) << "Failed to start capture: " << hr; - ongoing_captures_.erase(capture_source_->GetSourceId()); - callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, - /*frame=*/nullptr); - RecordWgcCapturerResult(WgcCapturerResult::kSessionStartFailure); - return; - } - } - - std::unique_ptr frame; - hr = capture_session->GetFrame(&frame); - if (FAILED(hr)) { - RTC_LOG(LS_ERROR) << "GetFrame failed: " << hr; - ongoing_captures_.erase(capture_source_->GetSourceId()); - callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, - /*frame=*/nullptr); - RecordWgcCapturerResult(WgcCapturerResult::kGetFrameFailure); - return; - } - - if (!frame) { - callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY, - /*frame=*/nullptr); - RecordWgcCapturerResult(WgcCapturerResult::kFrameDropped); - return; - } - - int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) / - rtc::kNumNanosecsPerMillisec; - RTC_HISTOGRAM_COUNTS_1000("WebRTC.DesktopCapture.Win.WgcCapturerFrameTime", - capture_time_ms); - frame->set_capture_time_ms(capture_time_ms); - frame->set_capturer_id(DesktopCapturerId::kWgcCapturerWin); - frame->set_may_contain_cursor(true); - frame->set_top_left(capture_source_->GetTopLeft()); - RecordWgcCapturerResult(WgcCapturerResult::kSuccess); - callback_->OnCaptureResult(DesktopCapturer::Result::SUCCESS, - std::move(frame)); -} - -bool WgcCapturerWin::IsSourceBeingCaptured(DesktopCapturer::SourceId id) { - std::map::iterator - session_iter = ongoing_captures_.find(id); - if (session_iter == ongoing_captures_.end()) - return false; - - return session_iter->second.IsCaptureStarted(); -} - -} // namespace webrtc diff --git a/application/remote_desk/webrtc/wgc_capturer_win.h b/application/remote_desk/webrtc/wgc_capturer_win.h deleted file mode 100644 index c7e497b..0000000 --- a/application/remote_desk/webrtc/wgc_capturer_win.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_ -#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_ - -#include -#include - -#include -#include - -#include "modules/desktop_capture/desktop_capture_options.h" -#include "modules/desktop_capture/desktop_capturer.h" -#include "modules/desktop_capture/win/screen_capture_utils.h" -#include "modules/desktop_capture/win/wgc_capture_session.h" -#include "modules/desktop_capture/win/wgc_capture_source.h" -#include "modules/desktop_capture/win/window_capture_utils.h" - -namespace webrtc { - -// WgcCapturerWin is initialized with an implementation of this base class, -// which it uses to find capturable sources of a particular type. This way, -// WgcCapturerWin can remain source-agnostic. -class SourceEnumerator { - public: - virtual ~SourceEnumerator() = default; - - virtual bool FindAllSources(DesktopCapturer::SourceList* sources) = 0; -}; - -class WindowEnumerator final : public SourceEnumerator { - public: - explicit WindowEnumerator(bool enumerate_current_process_windows) - : enumerate_current_process_windows_(enumerate_current_process_windows) {} - - WindowEnumerator(const WindowEnumerator&) = delete; - WindowEnumerator& operator=(const WindowEnumerator&) = delete; - - ~WindowEnumerator() override = default; - - bool FindAllSources(DesktopCapturer::SourceList* sources) override { - // WGC fails to capture windows with the WS_EX_TOOLWINDOW style, so we - // provide it as a filter to ensure windows with the style are not returned. - return window_capture_helper_.EnumerateCapturableWindows( - sources, enumerate_current_process_windows_, WS_EX_TOOLWINDOW); - } - - private: - WindowCaptureHelperWin window_capture_helper_; - bool enumerate_current_process_windows_; -}; - -class ScreenEnumerator final : public SourceEnumerator { - public: - ScreenEnumerator() = default; - - ScreenEnumerator(const ScreenEnumerator&) = delete; - ScreenEnumerator& operator=(const ScreenEnumerator&) = delete; - - ~ScreenEnumerator() override = default; - - bool FindAllSources(DesktopCapturer::SourceList* sources) override { - return webrtc::GetScreenList(sources); - } -}; - -// A capturer that uses the Window.Graphics.Capture APIs. It is suitable for -// both window and screen capture (but only one type per instance). Consumers -// should not instantiate this class directly, instead they should use -// |CreateRawWindowCapturer()| or |CreateRawScreenCapturer()| to receive a -// capturer appropriate for the type of source they want to capture. -class WgcCapturerWin : public DesktopCapturer { - public: - WgcCapturerWin(std::unique_ptr source_factory, - std::unique_ptr source_enumerator); - - WgcCapturerWin(const WgcCapturerWin&) = delete; - WgcCapturerWin& operator=(const WgcCapturerWin&) = delete; - - ~WgcCapturerWin() override; - - static std::unique_ptr CreateRawWindowCapturer( - const DesktopCaptureOptions& options); - - static std::unique_ptr CreateRawScreenCapturer( - const DesktopCaptureOptions& options); - - // DesktopCapturer interface. - bool GetSourceList(SourceList* sources) override; - bool SelectSource(SourceId id) override; - bool FocusOnSelectedSource() override; - void Start(Callback* callback) override; - void CaptureFrame() override; - - // Used in WgcCapturerTests. - bool IsSourceBeingCaptured(SourceId id); - - private: - // Factory to create a WgcCaptureSource for us whenever SelectSource is - // called. Initialized at construction with a source-specific implementation. - std::unique_ptr source_factory_; - - // The source enumerator helps us find capturable sources of the appropriate - // type. Initialized at construction with a source-specific implementation. - std::unique_ptr source_enumerator_; - - // The WgcCaptureSource represents the source we are capturing. It tells us - // if the source is capturable and it creates the GraphicsCaptureItem for us. - std::unique_ptr capture_source_; - - // A map of all the sources we are capturing and the associated - // WgcCaptureSession. Frames for the current source (indicated via - // SelectSource) will be retrieved from the appropriate session when - // requested via CaptureFrame. - // This helps us efficiently capture multiple sources (e.g. when consumers - // are trying to display a list of available capture targets with thumbnails). - std::map ongoing_captures_; - - // The callback that we deliver frames to, synchronously, before CaptureFrame - // returns. - Callback* callback_ = nullptr; - - // A Direct3D11 device that is shared amongst the WgcCaptureSessions, who - // require one to perform the capture. - Microsoft::WRL::ComPtr<::ID3D11Device> d3d11_device_; -}; - -} // namespace webrtc - -#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_ diff --git a/application/remote_desk/webrtc/wgc_capturer_win_unittest.cc b/application/remote_desk/webrtc/wgc_capturer_win_unittest.cc deleted file mode 100644 index 0f1dac0..0000000 --- a/application/remote_desk/webrtc/wgc_capturer_win_unittest.cc +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include "modules/desktop_capture/win/wgc_capturer_win.h" -#include -#include -#include -#include "modules/desktop_capture/desktop_capture_options.h" -#include "modules/desktop_capture/desktop_capture_types.h" -#include "modules/desktop_capture/desktop_capturer.h" -#include "modules/desktop_capture/win/test_support/test_window.h" -#include "modules/desktop_capture/win/window_capture_utils.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" -#include "rtc_base/thread.h" -#include "rtc_base/time_utils.h" -#include "rtc_base/win/scoped_com_initializer.h" -#include "rtc_base/win/windows_version.h" -#include "system_wrappers/include/metrics.h" -#include "test/gtest.h" -namespace webrtc { -namespace { -const char kWindowThreadName[] = "wgc_capturer_test_window_thread"; -const WCHAR kWindowTitle[] = L"WGC Capturer Test Window"; -const char kCapturerImplHistogram[] = - "WebRTC.DesktopCapture.Win.DesktopCapturerImpl"; -const char kCapturerResultHistogram[] = - "WebRTC.DesktopCapture.Win.WgcCapturerResult"; -const int kSuccess = 0; -const int kSessionStartFailure = 4; -const char kCaptureSessionResultHistogram[] = - "WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult"; -const int kSourceClosed = 1; -const char kCaptureTimeHistogram[] = - "WebRTC.DesktopCapture.Win.WgcCapturerFrameTime"; -const int kSmallWindowWidth = 200; -const int kSmallWindowHeight = 100; -const int kMediumWindowWidth = 300; -const int kMediumWindowHeight = 200; -const int kLargeWindowWidth = 400; -const int kLargeWindowHeight = 500; -// The size of the image we capture is slightly smaller than the actual size of -// the window. -const int kWindowWidthSubtrahend = 14; -const int kWindowHeightSubtrahend = 7; -// Custom message constants so we can direct our thread to close windows -// and quit running. -const UINT kNoOp = WM_APP; -const UINT kDestroyWindow = WM_APP + 1; -const UINT kQuitRunning = WM_APP + 2; -enum CaptureType { kWindowCapture = 0, kScreenCapture = 1 }; -} // namespace -class WgcCapturerWinTest : public ::testing::TestWithParam, - public DesktopCapturer::Callback { - public: - void SetUp() override { - if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_RS5) { - RTC_LOG(LS_INFO) - << "Skipping WgcCapturerWinTests on Windows versions < RS5."; - GTEST_SKIP(); - } - com_initializer_ = - std::make_unique(ScopedCOMInitializer::kMTA); - EXPECT_TRUE(com_initializer_->Succeeded()); - } - void SetUpForWindowCapture(int window_width = kMediumWindowWidth, - int window_height = kMediumWindowHeight) { - capturer_ = WgcCapturerWin::CreateRawWindowCapturer( - DesktopCaptureOptions::CreateDefault()); - CreateWindowOnSeparateThread(window_width, window_height); - StartWindowThreadMessageLoop(); - source_id_ = GetTestWindowIdFromSourceList(); - } - void SetUpForScreenCapture() { - capturer_ = WgcCapturerWin::CreateRawScreenCapturer( - DesktopCaptureOptions::CreateDefault()); - source_id_ = GetScreenIdFromSourceList(); - } - void TearDown() override { - if (window_open_) { - CloseTestWindow(); - } - } - // The window must live on a separate thread so that we can run a message pump - // without blocking the test thread. This is necessary if we are interested in - // having GraphicsCaptureItem events (i.e. the Closed event) fire, and it more - // closely resembles how capture works in the wild. - void CreateWindowOnSeparateThread(int window_width, int window_height) { - window_thread_ = rtc::Thread::Create(); - window_thread_->SetName(kWindowThreadName, nullptr); - window_thread_->Start(); - window_thread_->Invoke(RTC_FROM_HERE, [this, window_width, - window_height]() { - window_thread_id_ = GetCurrentThreadId(); - window_info_ = - CreateTestWindow(kWindowTitle, window_height, window_width); - window_open_ = true; - while (!IsWindowResponding(window_info_.hwnd)) { - RTC_LOG(LS_INFO) << "Waiting for test window to become responsive in " - "WgcWindowCaptureTest."; - } - while (!IsWindowValidAndVisible(window_info_.hwnd)) { - RTC_LOG(LS_INFO) << "Waiting for test window to be visible in " - "WgcWindowCaptureTest."; - } - }); - ASSERT_TRUE(window_thread_->RunningForTest()); - ASSERT_FALSE(window_thread_->IsCurrent()); - } - void StartWindowThreadMessageLoop() { - window_thread_->PostTask(RTC_FROM_HERE, [this]() { - MSG msg; - BOOL gm; - while ((gm = ::GetMessage(&msg, NULL, 0, 0)) != 0 && gm != -1) { - ::DispatchMessage(&msg); - if (msg.message == kDestroyWindow) { - DestroyTestWindow(window_info_); - } - if (msg.message == kQuitRunning) { - PostQuitMessage(0); - } - } - }); - } - void CloseTestWindow() { - ::PostThreadMessage(window_thread_id_, kDestroyWindow, 0, 0); - ::PostThreadMessage(window_thread_id_, kQuitRunning, 0, 0); - window_thread_->Stop(); - window_open_ = false; - } - DesktopCapturer::SourceId GetTestWindowIdFromSourceList() { - // Frequently, the test window will not show up in GetSourceList because it - // was created too recently. Since we are confident the window will be found - // eventually we loop here until we find it. - intptr_t src_id; - do { - DesktopCapturer::SourceList sources; - EXPECT_TRUE(capturer_->GetSourceList(&sources)); - auto it = std::find_if( - sources.begin(), sources.end(), - [&](const DesktopCapturer::Source& src) { - return src.id == reinterpret_cast(window_info_.hwnd); - }); - src_id = it->id; - } while (src_id != reinterpret_cast(window_info_.hwnd)); - return src_id; - } - DesktopCapturer::SourceId GetScreenIdFromSourceList() { - DesktopCapturer::SourceList sources; - EXPECT_TRUE(capturer_->GetSourceList(&sources)); - EXPECT_GT(sources.size(), 0ULL); - return sources[0].id; - } - void DoCapture() { - // Sometimes the first few frames are empty becaues the capture engine is - // still starting up. We also may drop a few frames when the window is - // resized or un-minimized. - do { - capturer_->CaptureFrame(); - } while (result_ == DesktopCapturer::Result::ERROR_TEMPORARY); - EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS); - EXPECT_TRUE(frame_); - EXPECT_GT(metrics::NumEvents(kCapturerResultHistogram, kSuccess), - successful_captures_); - ++successful_captures_; - } - void ValidateFrame(int expected_width, int expected_height) { - EXPECT_EQ(frame_->size().width(), expected_width - kWindowWidthSubtrahend); - EXPECT_EQ(frame_->size().height(), - expected_height - kWindowHeightSubtrahend); - // Verify the buffer contains as much data as it should, and that the right - // colors are found. - int data_length = frame_->stride() * frame_->size().height(); - // The first and last pixel should have the same color because they will be - // from the border of the window. - // Pixels have 4 bytes of data so the whole pixel needs a uint32_t to fit. - uint32_t first_pixel = static_cast(*frame_->data()); - uint32_t last_pixel = static_cast( - *(frame_->data() + data_length - DesktopFrame::kBytesPerPixel)); - EXPECT_EQ(first_pixel, last_pixel); - // Let's also check a pixel from the middle of the content area, which the - // TestWindow will paint a consistent color for us to verify. - uint8_t* middle_pixel = frame_->data() + (data_length / 2); - int sub_pixel_offset = DesktopFrame::kBytesPerPixel / 4; - EXPECT_EQ(*middle_pixel, kTestWindowBValue); - middle_pixel += sub_pixel_offset; - EXPECT_EQ(*middle_pixel, kTestWindowGValue); - middle_pixel += sub_pixel_offset; - EXPECT_EQ(*middle_pixel, kTestWindowRValue); - middle_pixel += sub_pixel_offset; - // The window is opaque so we expect 0xFF for the Alpha channel. - EXPECT_EQ(*middle_pixel, 0xFF); - } - // DesktopCapturer::Callback interface - // The capturer synchronously invokes this method before |CaptureFrame()| - // returns. - void OnCaptureResult(DesktopCapturer::Result result, - std::unique_ptr frame) override { - result_ = result; - frame_ = std::move(frame); - } - protected: - std::unique_ptr com_initializer_; - DWORD window_thread_id_; - std::unique_ptr window_thread_; - WindowInfo window_info_; - intptr_t source_id_; - bool window_open_ = false; - DesktopCapturer::Result result_; - int successful_captures_ = 0; - std::unique_ptr frame_; - std::unique_ptr capturer_; -}; -TEST_P(WgcCapturerWinTest, SelectValidSource) { - if (GetParam() == CaptureType::kWindowCapture) { - SetUpForWindowCapture(); - } else { - SetUpForScreenCapture(); - } - EXPECT_TRUE(capturer_->SelectSource(source_id_)); -} -TEST_P(WgcCapturerWinTest, SelectInvalidSource) { - if (GetParam() == CaptureType::kWindowCapture) { - capturer_ = WgcCapturerWin::CreateRawWindowCapturer( - DesktopCaptureOptions::CreateDefault()); - source_id_ = kNullWindowId; - } else { - capturer_ = WgcCapturerWin::CreateRawScreenCapturer( - DesktopCaptureOptions::CreateDefault()); - source_id_ = kInvalidScreenId; - } - EXPECT_FALSE(capturer_->SelectSource(source_id_)); -} -TEST_P(WgcCapturerWinTest, Capture) { - if (GetParam() == CaptureType::kWindowCapture) { - SetUpForWindowCapture(); - } else { - SetUpForScreenCapture(); - } - EXPECT_TRUE(capturer_->SelectSource(source_id_)); - capturer_->Start(this); - EXPECT_GE(metrics::NumEvents(kCapturerImplHistogram, - DesktopCapturerId::kWgcCapturerWin), - 1); - DoCapture(); - EXPECT_GT(frame_->size().width(), 0); - EXPECT_GT(frame_->size().height(), 0); -} -TEST_P(WgcCapturerWinTest, CaptureTime) { - if (GetParam() == CaptureType::kWindowCapture) { - SetUpForWindowCapture(); - } else { - SetUpForScreenCapture(); - } - EXPECT_TRUE(capturer_->SelectSource(source_id_)); - capturer_->Start(this); - int64_t start_time; - do { - start_time = rtc::TimeNanos(); - capturer_->CaptureFrame(); - } while (result_ == DesktopCapturer::Result::ERROR_TEMPORARY); - int capture_time_ms = - (rtc::TimeNanos() - start_time) / rtc::kNumNanosecsPerMillisec; - EXPECT_TRUE(frame_); - // The test may measure the time slightly differently than the capturer. So we - // just check if it's within 5 ms. - EXPECT_NEAR(frame_->capture_time_ms(), capture_time_ms, 5); - EXPECT_GE( - metrics::NumEvents(kCaptureTimeHistogram, frame_->capture_time_ms()), 1); -} -INSTANTIATE_TEST_SUITE_P(SourceAgnostic, - WgcCapturerWinTest, - ::testing::Values(CaptureType::kWindowCapture, - CaptureType::kScreenCapture)); -// Monitor specific tests. -TEST_F(WgcCapturerWinTest, FocusOnMonitor) { - SetUpForScreenCapture(); - EXPECT_TRUE(capturer_->SelectSource(0)); - // You can't set focus on a monitor. - EXPECT_FALSE(capturer_->FocusOnSelectedSource()); -} -TEST_F(WgcCapturerWinTest, CaptureAllMonitors) { - SetUpForScreenCapture(); - EXPECT_TRUE(capturer_->SelectSource(kFullDesktopScreenId)); - capturer_->Start(this); - DoCapture(); - EXPECT_GT(frame_->size().width(), 0); - EXPECT_GT(frame_->size().height(), 0); -} -// Window specific tests. -TEST_F(WgcCapturerWinTest, FocusOnWindow) { - capturer_ = WgcCapturerWin::CreateRawWindowCapturer( - DesktopCaptureOptions::CreateDefault()); - window_info_ = CreateTestWindow(kWindowTitle); - source_id_ = GetScreenIdFromSourceList(); - EXPECT_TRUE(capturer_->SelectSource(source_id_)); - EXPECT_TRUE(capturer_->FocusOnSelectedSource()); - HWND hwnd = reinterpret_cast(source_id_); - EXPECT_EQ(hwnd, ::GetActiveWindow()); - EXPECT_EQ(hwnd, ::GetForegroundWindow()); - EXPECT_EQ(hwnd, ::GetFocus()); - DestroyTestWindow(window_info_); -} -TEST_F(WgcCapturerWinTest, SelectMinimizedWindow) { - SetUpForWindowCapture(); - MinimizeTestWindow(reinterpret_cast(source_id_)); - EXPECT_FALSE(capturer_->SelectSource(source_id_)); - UnminimizeTestWindow(reinterpret_cast(source_id_)); - EXPECT_TRUE(capturer_->SelectSource(source_id_)); -} -TEST_F(WgcCapturerWinTest, SelectClosedWindow) { - SetUpForWindowCapture(); - EXPECT_TRUE(capturer_->SelectSource(source_id_)); - CloseTestWindow(); - EXPECT_FALSE(capturer_->SelectSource(source_id_)); -} -TEST_F(WgcCapturerWinTest, UnsupportedWindowStyle) { - // Create a window with the WS_EX_TOOLWINDOW style, which WGC does not - // support. - window_info_ = CreateTestWindow(kWindowTitle, kMediumWindowWidth, - kMediumWindowHeight, WS_EX_TOOLWINDOW); - capturer_ = WgcCapturerWin::CreateRawWindowCapturer( - DesktopCaptureOptions::CreateDefault()); - DesktopCapturer::SourceList sources; - EXPECT_TRUE(capturer_->GetSourceList(&sources)); - auto it = std::find_if( - sources.begin(), sources.end(), [&](const DesktopCapturer::Source& src) { - return src.id == reinterpret_cast(window_info_.hwnd); - }); - // We should not find the window, since we filter for unsupported styles. - EXPECT_EQ(it, sources.end()); - DestroyTestWindow(window_info_); -} -TEST_F(WgcCapturerWinTest, IncreaseWindowSizeMidCapture) { - SetUpForWindowCapture(kSmallWindowWidth, kSmallWindowHeight); - EXPECT_TRUE(capturer_->SelectSource(source_id_)); - capturer_->Start(this); - DoCapture(); - ValidateFrame(kSmallWindowWidth, kSmallWindowHeight); - ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight); - DoCapture(); - // We don't expect to see the new size until the next capture, as the frame - // pool hadn't had a chance to resize yet to fit the new, larger image. - DoCapture(); - ValidateFrame(kSmallWindowWidth, kMediumWindowHeight); - ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight); - DoCapture(); - DoCapture(); - ValidateFrame(kLargeWindowWidth, kMediumWindowHeight); -} -TEST_F(WgcCapturerWinTest, ReduceWindowSizeMidCapture) { - SetUpForWindowCapture(kLargeWindowWidth, kLargeWindowHeight); - EXPECT_TRUE(capturer_->SelectSource(source_id_)); - capturer_->Start(this); - DoCapture(); - ValidateFrame(kLargeWindowWidth, kLargeWindowHeight); - ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight); - // We expect to see the new size immediately because the image data has shrunk - // and will fit in the existing buffer. - DoCapture(); - ValidateFrame(kLargeWindowWidth, kMediumWindowHeight); - ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight); - DoCapture(); - ValidateFrame(kSmallWindowWidth, kMediumWindowHeight); -} -TEST_F(WgcCapturerWinTest, MinimizeWindowMidCapture) { - SetUpForWindowCapture(); - EXPECT_TRUE(capturer_->SelectSource(source_id_)); - capturer_->Start(this); - // Minmize the window and capture should continue but return temporary errors. - MinimizeTestWindow(window_info_.hwnd); - for (int i = 0; i < 10; ++i) { - capturer_->CaptureFrame(); - EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_TEMPORARY); - } - // Reopen the window and the capture should continue normally. - UnminimizeTestWindow(window_info_.hwnd); - DoCapture(); - // We can't verify the window size here because the test window does not - // repaint itself after it is unminimized, but capturing successfully is still - // a good test. -} -TEST_F(WgcCapturerWinTest, CloseWindowMidCapture) { - SetUpForWindowCapture(); - EXPECT_TRUE(capturer_->SelectSource(source_id_)); - capturer_->Start(this); - DoCapture(); - ValidateFrame(kMediumWindowWidth, kMediumWindowHeight); - CloseTestWindow(); - // We need to call GetMessage to trigger the Closed event and the capturer's - // event handler for it. If we are too early and the Closed event hasn't - // arrived yet we should keep trying until the capturer receives it and stops. - auto* wgc_capturer = static_cast(capturer_.get()); - while (wgc_capturer->IsSourceBeingCaptured(source_id_)) { - // Since the capturer handles the Closed message, there will be no message - // for us and GetMessage will hang, unless we send ourselves a message - // first. - ::PostThreadMessage(GetCurrentThreadId(), kNoOp, 0, 0); - MSG msg; - ::GetMessage(&msg, NULL, 0, 0); - ::DispatchMessage(&msg); - } - // Occasionally, one last frame will have made it into the frame pool before - // the window closed. The first call will consume it, and in that case we need - // to make one more call to CaptureFrame. - capturer_->CaptureFrame(); - if (result_ == DesktopCapturer::Result::SUCCESS) - capturer_->CaptureFrame(); - EXPECT_GE(metrics::NumEvents(kCapturerResultHistogram, kSessionStartFailure), - 1); - EXPECT_GE(metrics::NumEvents(kCaptureSessionResultHistogram, kSourceClosed), - 1); - EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_PERMANENT); -} -} // namespace webrtc diff --git a/application/remote_desk/webrtc/wgc_desktop_frame.cc b/application/remote_desk/webrtc/wgc_desktop_frame.cc deleted file mode 100644 index 9e37fc6..0000000 --- a/application/remote_desk/webrtc/wgc_desktop_frame.cc +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include "modules/desktop_capture/win/wgc_desktop_frame.h" -#include -namespace webrtc { -WgcDesktopFrame::WgcDesktopFrame(DesktopSize size, - int stride, - std::vector&& image_data) - : DesktopFrame(size, stride, image_data.data(), nullptr), - image_data_(std::move(image_data)) {} -WgcDesktopFrame::~WgcDesktopFrame() = default; -} // namespace webrtc diff --git a/application/remote_desk/webrtc/wgc_desktop_frame.h b/application/remote_desk/webrtc/wgc_desktop_frame.h deleted file mode 100644 index e64fb69..0000000 --- a/application/remote_desk/webrtc/wgc_desktop_frame.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#ifndef MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_ -#define MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_ -#include -#include -#include -#include -#include "desktop_frame.h" -#include "desktop_geometry.h" -namespace webrtc { -// DesktopFrame implementation used by capturers that use the -// Windows.Graphics.Capture API. -class WgcDesktopFrame final : public DesktopFrame { - public: - // WgcDesktopFrame receives an rvalue reference to the |image_data| vector - // so that it can take ownership of it (and avoid a copy). - WgcDesktopFrame(DesktopSize size, - int stride, - std::vector&& image_data); - WgcDesktopFrame(const WgcDesktopFrame&) = delete; - WgcDesktopFrame& operator=(const WgcDesktopFrame&) = delete; - ~WgcDesktopFrame() override; - private: - std::vector image_data_; -}; -} // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_ diff --git a/application/remote_desk/xmake.lua b/application/remote_desk/xmake.lua index b2d4175..713200b 100644 --- a/application/remote_desk/xmake.lua +++ b/application/remote_desk/xmake.lua @@ -29,16 +29,30 @@ target("log") add_headerfiles("../../src/log/log.h") add_includedirs("../../src/log", {public = true}) -target("remote_desk") - set_kind("binary") - add_deps("projectx") +target("screen_capture") + set_kind("static") add_packages("log") - add_packages("ffmpeg") - add_packages("vcpkg::sdl2") - add_links("avfilter", "avdevice", "avformat", "avcodec", "swscale", "swresample", "avutil") - add_files("dll/*.cpp") + add_files("screen_capture/*.cpp") + add_includedirs("screen_capture", {public = true}) + +target("remote_desk_server") + set_kind("binary") + add_packages("log", "ffmpeg") + add_deps("projectx", "screen_capture") + add_files("remote_desk_server/*.cpp") add_includedirs("../../src/interface") - add_links("SDL2-static", "SDL2main", "Shell32", "gdi32", "winmm", - "setupapi", "version", "WindowsApp", "Imm32", "avutil") + -- add_links("avformat", "swscale") + +-- target("remote_desk") +-- set_kind("binary") +-- add_deps("projectx") +-- add_packages("log") +-- add_packages("ffmpeg") +-- add_packages("vcpkg::sdl2") +-- add_links("avfilter", "avdevice", "avformat", "avcodec", "swscale", "swresample", "avutil") +-- add_files("**.cpp") +-- add_includedirs("../../src/interface") +-- add_links("SDL2-static", "SDL2main", "Shell32", "gdi32", "winmm", +-- "setupapi", "version", "WindowsApp", "Imm32", "avutil") diff --git a/src/interface/x.h b/src/interface/x.h index 29228b0..d4209e8 100644 --- a/src/interface/x.h +++ b/src/interface/x.h @@ -5,6 +5,7 @@ #include enum ws_status { WS_CONNECTING = 0, WS_OPEN, WS_FAILED, WS_CLOSED, WS_UNKNOWN }; +enum DATA_TYPE { VIDEO = 0, AUDIO, USER }; #ifdef __cplusplus extern "C" { @@ -30,7 +31,8 @@ int CreateConnection(PeerPtr* peer_ptr, const char* transmission_id, int JoinConnection(PeerPtr* peer_ptr, const char* transmission_id, const char* user_id); -int SendData(PeerPtr* peer_ptr, const char* data, size_t size); +int SendData(PeerPtr* peer_ptr, DATA_TYPE data_type, const char* data, + size_t size); #ifdef __cplusplus } diff --git a/src/media/video/decode/nvcodec/NvDecoder.cpp b/src/media/video/decode/nvcodec/NvDecoder.cpp new file mode 100644 index 0000000..4d13f3e --- /dev/null +++ b/src/media/video/decode/nvcodec/NvDecoder.cpp @@ -0,0 +1,774 @@ +/* + * Copyright 2017-2020 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +#include "NvDecoder.h" + +#include +#include +#include +#include + +#include "nvcuvid.h" + +#define START_TIMER auto start = std::chrono::high_resolution_clock::now(); +#define STOP_TIMER(print_message) \ + std::cout << print_message \ + << std::chrono::duration_cast( \ + std::chrono::high_resolution_clock::now() - start) \ + .count() \ + << " ms " << std::endl; + +#define CUDA_DRVAPI_CALL(call) \ + do { \ + CUresult err__ = call; \ + if (err__ != CUDA_SUCCESS) { \ + const char *szErrName = NULL; \ + cuGetErrorName(err__, &szErrName); \ + std::ostringstream errorLog; \ + errorLog << "CUDA driver API error " << szErrName; \ + throw NVDECException::makeNVDECException( \ + errorLog.str(), err__, __FUNCTION__, __FILE__, __LINE__); \ + } \ + } while (0) + +static const char *GetVideoCodecString(cudaVideoCodec eCodec) { + static struct { + cudaVideoCodec eCodec; + const char *name; + } aCodecName[] = { + {cudaVideoCodec_MPEG1, "MPEG-1"}, + {cudaVideoCodec_MPEG2, "MPEG-2"}, + {cudaVideoCodec_MPEG4, "MPEG-4 (ASP)"}, + {cudaVideoCodec_VC1, "VC-1/WMV"}, + {cudaVideoCodec_H264, "AVC/H.264"}, + {cudaVideoCodec_JPEG, "M-JPEG"}, + {cudaVideoCodec_H264_SVC, "H.264/SVC"}, + {cudaVideoCodec_H264_MVC, "H.264/MVC"}, + {cudaVideoCodec_HEVC, "H.265/HEVC"}, + {cudaVideoCodec_VP8, "VP8"}, + {cudaVideoCodec_VP9, "VP9"}, + {cudaVideoCodec_AV1, "AV1"}, + {cudaVideoCodec_NumCodecs, "Invalid"}, + {cudaVideoCodec_YUV420, "YUV 4:2:0"}, + {cudaVideoCodec_YV12, "YV12 4:2:0"}, + {cudaVideoCodec_NV12, "NV12 4:2:0"}, + {cudaVideoCodec_YUYV, "YUYV 4:2:2"}, + {cudaVideoCodec_UYVY, "UYVY 4:2:2"}, + }; + + if (eCodec >= 0 && eCodec <= cudaVideoCodec_NumCodecs) { + return aCodecName[eCodec].name; + } + for (int i = cudaVideoCodec_NumCodecs + 1; + i < sizeof(aCodecName) / sizeof(aCodecName[0]); i++) { + if (eCodec == aCodecName[i].eCodec) { + return aCodecName[eCodec].name; + } + } + return "Unknown"; +} + +static const char *GetVideoChromaFormatString( + cudaVideoChromaFormat eChromaFormat) { + static struct { + cudaVideoChromaFormat eChromaFormat; + const char *name; + } aChromaFormatName[] = { + {cudaVideoChromaFormat_Monochrome, "YUV 400 (Monochrome)"}, + {cudaVideoChromaFormat_420, "YUV 420"}, + {cudaVideoChromaFormat_422, "YUV 422"}, + {cudaVideoChromaFormat_444, "YUV 444"}, + }; + + if (eChromaFormat >= 0 && eChromaFormat < sizeof(aChromaFormatName) / + sizeof(aChromaFormatName[0])) { + return aChromaFormatName[eChromaFormat].name; + } + return "Unknown"; +} + +static float GetChromaHeightFactor(cudaVideoSurfaceFormat eSurfaceFormat) { + float factor = 0.5; + switch (eSurfaceFormat) { + case cudaVideoSurfaceFormat_NV12: + case cudaVideoSurfaceFormat_P016: + factor = 0.5; + break; + case cudaVideoSurfaceFormat_YUV444: + case cudaVideoSurfaceFormat_YUV444_16Bit: + factor = 1.0; + break; + } + + return factor; +} + +static int GetChromaPlaneCount(cudaVideoSurfaceFormat eSurfaceFormat) { + int numPlane = 1; + switch (eSurfaceFormat) { + case cudaVideoSurfaceFormat_NV12: + case cudaVideoSurfaceFormat_P016: + numPlane = 1; + break; + case cudaVideoSurfaceFormat_YUV444: + case cudaVideoSurfaceFormat_YUV444_16Bit: + numPlane = 2; + break; + } + + return numPlane; +} + +/** + * @brief This function is used to get codec string from codec id + */ +const char *NvDecoder::GetCodecString(cudaVideoCodec eCodec) { + return GetVideoCodecString(eCodec); +} + +/* Called when the parser encounters sequence header for AV1 SVC content + * return value interpretation: + * < 0 : fail, >=0: succeeded (bit 0-9: currOperatingPoint, bit 10-10: + * bDispAllLayer, bit 11-30: reserved, must be set 0) + */ +int NvDecoder::GetOperatingPoint(CUVIDOPERATINGPOINTINFO *pOPInfo) { + if (pOPInfo->codec == cudaVideoCodec_AV1) { + if (pOPInfo->av1.operating_points_cnt > 1) { + // clip has SVC enabled + if (m_nOperatingPoint >= pOPInfo->av1.operating_points_cnt) + m_nOperatingPoint = 0; + + printf("AV1 SVC clip: operating point count %d ", + pOPInfo->av1.operating_points_cnt); + printf("Selected operating point: %d, IDC 0x%x bOutputAllLayers %d\n", + m_nOperatingPoint, + pOPInfo->av1.operating_points_idc[m_nOperatingPoint], + m_bDispAllLayers); + return (m_nOperatingPoint | (m_bDispAllLayers << 10)); + } + } + return -1; +} + +/* Return value from HandleVideoSequence() are interpreted as : + * 0: fail, 1: succeeded, > 1: override dpb size of parser (set by + * CUVIDPARSERPARAMS::ulMaxNumDecodeSurfaces while creating parser) + */ +int NvDecoder::HandleVideoSequence(CUVIDEOFORMAT *pVideoFormat) { + START_TIMER + m_videoInfo.str(""); + m_videoInfo.clear(); + m_videoInfo << "Video Input Information" << std::endl + << "\tCodec : " << GetVideoCodecString(pVideoFormat->codec) + << std::endl + << "\tFrame rate : " << pVideoFormat->frame_rate.numerator + << "/" << pVideoFormat->frame_rate.denominator << " = " + << 1.0 * pVideoFormat->frame_rate.numerator / + pVideoFormat->frame_rate.denominator + << " fps" << std::endl + << "\tSequence : " + << (pVideoFormat->progressive_sequence ? "Progressive" + : "Interlaced") + << std::endl + << "\tCoded size : [" << pVideoFormat->coded_width << ", " + << pVideoFormat->coded_height << "]" << std::endl + << "\tDisplay area : [" << pVideoFormat->display_area.left << ", " + << pVideoFormat->display_area.top << ", " + << pVideoFormat->display_area.right << ", " + << pVideoFormat->display_area.bottom << "]" << std::endl + << "\tChroma : " + << GetVideoChromaFormatString(pVideoFormat->chroma_format) + << std::endl + << "\tBit depth : " << pVideoFormat->bit_depth_luma_minus8 + 8; + m_videoInfo << std::endl; + + int nDecodeSurface = pVideoFormat->min_num_decode_surfaces; + + CUVIDDECODECAPS decodecaps; + memset(&decodecaps, 0, sizeof(decodecaps)); + + decodecaps.eCodecType = pVideoFormat->codec; + decodecaps.eChromaFormat = pVideoFormat->chroma_format; + decodecaps.nBitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8; + + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + NVDEC_API_CALL(cuvidGetDecoderCaps(&decodecaps)); + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + + if (!decodecaps.bIsSupported) { + NVDEC_THROW_ERROR("Codec not supported on this GPU", + CUDA_ERROR_NOT_SUPPORTED); + return nDecodeSurface; + } + + if ((pVideoFormat->coded_width > decodecaps.nMaxWidth) || + (pVideoFormat->coded_height > decodecaps.nMaxHeight)) { + std::ostringstream errorString; + errorString << std::endl + << "Resolution : " << pVideoFormat->coded_width << "x" + << pVideoFormat->coded_height << std::endl + << "Max Supported (wxh) : " << decodecaps.nMaxWidth << "x" + << decodecaps.nMaxHeight << std::endl + << "Resolution not supported on this GPU"; + + const std::string cErr = errorString.str(); + NVDEC_THROW_ERROR(cErr, CUDA_ERROR_NOT_SUPPORTED); + return nDecodeSurface; + } + + if ((pVideoFormat->coded_width >> 4) * (pVideoFormat->coded_height >> 4) > + decodecaps.nMaxMBCount) { + std::ostringstream errorString; + errorString << std::endl + << "MBCount : " + << (pVideoFormat->coded_width >> 4) * + (pVideoFormat->coded_height >> 4) + << std::endl + << "Max Supported mbcnt : " << decodecaps.nMaxMBCount + << std::endl + << "MBCount not supported on this GPU"; + + const std::string cErr = errorString.str(); + NVDEC_THROW_ERROR(cErr, CUDA_ERROR_NOT_SUPPORTED); + return nDecodeSurface; + } + + if (m_nWidth && m_nLumaHeight && m_nChromaHeight) { + // cuvidCreateDecoder() has been called before, and now there's possible + // config change + return ReconfigureDecoder(pVideoFormat); + } + + // eCodec has been set in the constructor (for parser). Here it's set again + // for potential correction + m_eCodec = pVideoFormat->codec; + m_eChromaFormat = pVideoFormat->chroma_format; + m_nBitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8; + m_nBPP = m_nBitDepthMinus8 > 0 ? 2 : 1; + + // Set the output surface format same as chroma format + if (m_eChromaFormat == cudaVideoChromaFormat_420 || + cudaVideoChromaFormat_Monochrome) + m_eOutputFormat = pVideoFormat->bit_depth_luma_minus8 + ? cudaVideoSurfaceFormat_P016 + : cudaVideoSurfaceFormat_NV12; + else if (m_eChromaFormat == cudaVideoChromaFormat_444) + m_eOutputFormat = pVideoFormat->bit_depth_luma_minus8 + ? cudaVideoSurfaceFormat_YUV444_16Bit + : cudaVideoSurfaceFormat_YUV444; + else if (m_eChromaFormat == cudaVideoChromaFormat_422) + m_eOutputFormat = + cudaVideoSurfaceFormat_NV12; // no 4:2:2 output format supported yet so + // make 420 default + + // Check if output format supported. If not, check falback options + if (!(decodecaps.nOutputFormatMask & (1 << m_eOutputFormat))) { + if (decodecaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_NV12)) + m_eOutputFormat = cudaVideoSurfaceFormat_NV12; + else if (decodecaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_P016)) + m_eOutputFormat = cudaVideoSurfaceFormat_P016; + else if (decodecaps.nOutputFormatMask & + (1 << cudaVideoSurfaceFormat_YUV444)) + m_eOutputFormat = cudaVideoSurfaceFormat_YUV444; + else if (decodecaps.nOutputFormatMask & + (1 << cudaVideoSurfaceFormat_YUV444_16Bit)) + m_eOutputFormat = cudaVideoSurfaceFormat_YUV444_16Bit; + else + NVDEC_THROW_ERROR("No supported output format found", + CUDA_ERROR_NOT_SUPPORTED); + } + m_videoFormat = *pVideoFormat; + + CUVIDDECODECREATEINFO videoDecodeCreateInfo = {0}; + videoDecodeCreateInfo.CodecType = pVideoFormat->codec; + videoDecodeCreateInfo.ChromaFormat = pVideoFormat->chroma_format; + videoDecodeCreateInfo.OutputFormat = m_eOutputFormat; + videoDecodeCreateInfo.bitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8; + if (pVideoFormat->progressive_sequence) + videoDecodeCreateInfo.DeinterlaceMode = cudaVideoDeinterlaceMode_Weave; + else + videoDecodeCreateInfo.DeinterlaceMode = cudaVideoDeinterlaceMode_Adaptive; + videoDecodeCreateInfo.ulNumOutputSurfaces = 2; + // With PreferCUVID, JPEG is still decoded by CUDA while video is decoded by + // NVDEC hardware + videoDecodeCreateInfo.ulCreationFlags = cudaVideoCreate_PreferCUVID; + videoDecodeCreateInfo.ulNumDecodeSurfaces = nDecodeSurface; + videoDecodeCreateInfo.vidLock = m_ctxLock; + videoDecodeCreateInfo.ulWidth = pVideoFormat->coded_width; + videoDecodeCreateInfo.ulHeight = pVideoFormat->coded_height; + // AV1 has max width/height of sequence in sequence header + if (pVideoFormat->codec == cudaVideoCodec_AV1 && + pVideoFormat->seqhdr_data_length > 0) { + // dont overwrite if it is already set from cmdline or reconfig.txt + if (!(m_nMaxWidth > pVideoFormat->coded_width || + m_nMaxHeight > pVideoFormat->coded_height)) { + CUVIDEOFORMATEX *vidFormatEx = (CUVIDEOFORMATEX *)pVideoFormat; + m_nMaxWidth = vidFormatEx->av1.max_width; + m_nMaxHeight = vidFormatEx->av1.max_height; + } + } + if (m_nMaxWidth < (int)pVideoFormat->coded_width) + m_nMaxWidth = pVideoFormat->coded_width; + if (m_nMaxHeight < (int)pVideoFormat->coded_height) + m_nMaxHeight = pVideoFormat->coded_height; + videoDecodeCreateInfo.ulMaxWidth = m_nMaxWidth; + videoDecodeCreateInfo.ulMaxHeight = m_nMaxHeight; + + if (!(m_cropRect.r && m_cropRect.b) && !(m_resizeDim.w && m_resizeDim.h)) { + m_nWidth = + pVideoFormat->display_area.right - pVideoFormat->display_area.left; + m_nLumaHeight = + pVideoFormat->display_area.bottom - pVideoFormat->display_area.top; + videoDecodeCreateInfo.ulTargetWidth = pVideoFormat->coded_width; + videoDecodeCreateInfo.ulTargetHeight = pVideoFormat->coded_height; + } else { + if (m_resizeDim.w && m_resizeDim.h) { + videoDecodeCreateInfo.display_area.left = pVideoFormat->display_area.left; + videoDecodeCreateInfo.display_area.top = pVideoFormat->display_area.top; + videoDecodeCreateInfo.display_area.right = + pVideoFormat->display_area.right; + videoDecodeCreateInfo.display_area.bottom = + pVideoFormat->display_area.bottom; + m_nWidth = m_resizeDim.w; + m_nLumaHeight = m_resizeDim.h; + } + + if (m_cropRect.r && m_cropRect.b) { + videoDecodeCreateInfo.display_area.left = m_cropRect.l; + videoDecodeCreateInfo.display_area.top = m_cropRect.t; + videoDecodeCreateInfo.display_area.right = m_cropRect.r; + videoDecodeCreateInfo.display_area.bottom = m_cropRect.b; + m_nWidth = m_cropRect.r - m_cropRect.l; + m_nLumaHeight = m_cropRect.b - m_cropRect.t; + } + videoDecodeCreateInfo.ulTargetWidth = m_nWidth; + videoDecodeCreateInfo.ulTargetHeight = m_nLumaHeight; + } + + m_nChromaHeight = + (int)(ceil(m_nLumaHeight * GetChromaHeightFactor(m_eOutputFormat))); + m_nNumChromaPlanes = GetChromaPlaneCount(m_eOutputFormat); + m_nSurfaceHeight = videoDecodeCreateInfo.ulTargetHeight; + m_nSurfaceWidth = videoDecodeCreateInfo.ulTargetWidth; + m_displayRect.b = videoDecodeCreateInfo.display_area.bottom; + m_displayRect.t = videoDecodeCreateInfo.display_area.top; + m_displayRect.l = videoDecodeCreateInfo.display_area.left; + m_displayRect.r = videoDecodeCreateInfo.display_area.right; + + m_videoInfo << "Video Decoding Params:" << std::endl + << "\tNum Surfaces : " + << videoDecodeCreateInfo.ulNumDecodeSurfaces << std::endl + << "\tCrop : [" << videoDecodeCreateInfo.display_area.left + << ", " << videoDecodeCreateInfo.display_area.top << ", " + << videoDecodeCreateInfo.display_area.right << ", " + << videoDecodeCreateInfo.display_area.bottom << "]" << std::endl + << "\tResize : " << videoDecodeCreateInfo.ulTargetWidth + << "x" << videoDecodeCreateInfo.ulTargetHeight << std::endl + << "\tDeinterlace : " + << std::vector{ + "Weave", "Bob", + "Adaptive"}[videoDecodeCreateInfo.DeinterlaceMode]; + m_videoInfo << std::endl; + + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + NVDEC_API_CALL(cuvidCreateDecoder(&m_hDecoder, &videoDecodeCreateInfo)); + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + STOP_TIMER("Session Initialization Time: "); + return nDecodeSurface; +} + +int NvDecoder::ReconfigureDecoder(CUVIDEOFORMAT *pVideoFormat) { + if (pVideoFormat->bit_depth_luma_minus8 != + m_videoFormat.bit_depth_luma_minus8 || + pVideoFormat->bit_depth_chroma_minus8 != + m_videoFormat.bit_depth_chroma_minus8) { + NVDEC_THROW_ERROR("Reconfigure Not supported for bit depth change", + CUDA_ERROR_NOT_SUPPORTED); + } + + if (pVideoFormat->chroma_format != m_videoFormat.chroma_format) { + NVDEC_THROW_ERROR("Reconfigure Not supported for chroma format change", + CUDA_ERROR_NOT_SUPPORTED); + } + + bool bDecodeResChange = + !(pVideoFormat->coded_width == m_videoFormat.coded_width && + pVideoFormat->coded_height == m_videoFormat.coded_height); + bool bDisplayRectChange = !( + pVideoFormat->display_area.bottom == m_videoFormat.display_area.bottom && + pVideoFormat->display_area.top == m_videoFormat.display_area.top && + pVideoFormat->display_area.left == m_videoFormat.display_area.left && + pVideoFormat->display_area.right == m_videoFormat.display_area.right); + + int nDecodeSurface = pVideoFormat->min_num_decode_surfaces; + + if ((pVideoFormat->coded_width > m_nMaxWidth) || + (pVideoFormat->coded_height > m_nMaxHeight)) { + // For VP9, let driver handle the change if new width/height > + // maxwidth/maxheight + if ((m_eCodec != cudaVideoCodec_VP9) || m_bReconfigExternal) { + NVDEC_THROW_ERROR( + "Reconfigure Not supported when width/height > maxwidth/maxheight", + CUDA_ERROR_NOT_SUPPORTED); + } + return 1; + } + + if (!bDecodeResChange && !m_bReconfigExtPPChange) { + // if the coded_width/coded_height hasn't changed but display resolution has + // changed, then need to update width/height for correct output without + // cropping. Example : 1920x1080 vs 1920x1088 + if (bDisplayRectChange) { + m_nWidth = + pVideoFormat->display_area.right - pVideoFormat->display_area.left; + m_nLumaHeight = + pVideoFormat->display_area.bottom - pVideoFormat->display_area.top; + m_nChromaHeight = + (int)ceil(m_nLumaHeight * GetChromaHeightFactor(m_eOutputFormat)); + m_nNumChromaPlanes = GetChromaPlaneCount(m_eOutputFormat); + } + + // no need for reconfigureDecoder(). Just return + return 1; + } + + CUVIDRECONFIGUREDECODERINFO reconfigParams = {0}; + + reconfigParams.ulWidth = m_videoFormat.coded_width = + pVideoFormat->coded_width; + reconfigParams.ulHeight = m_videoFormat.coded_height = + pVideoFormat->coded_height; + + // Dont change display rect and get scaled output from decoder. This will help + // display app to present apps smoothly + reconfigParams.display_area.bottom = m_displayRect.b; + reconfigParams.display_area.top = m_displayRect.t; + reconfigParams.display_area.left = m_displayRect.l; + reconfigParams.display_area.right = m_displayRect.r; + reconfigParams.ulTargetWidth = m_nSurfaceWidth; + reconfigParams.ulTargetHeight = m_nSurfaceHeight; + + // If external reconfigure is called along with resolution change even if post + // processing params is not changed, do full reconfigure params update + if ((m_bReconfigExternal && bDecodeResChange) || m_bReconfigExtPPChange) { + // update display rect and target resolution if requested explicitely + m_bReconfigExternal = false; + m_bReconfigExtPPChange = false; + m_videoFormat = *pVideoFormat; + if (!(m_cropRect.r && m_cropRect.b) && !(m_resizeDim.w && m_resizeDim.h)) { + m_nWidth = + pVideoFormat->display_area.right - pVideoFormat->display_area.left; + m_nLumaHeight = + pVideoFormat->display_area.bottom - pVideoFormat->display_area.top; + reconfigParams.ulTargetWidth = pVideoFormat->coded_width; + reconfigParams.ulTargetHeight = pVideoFormat->coded_height; + } else { + if (m_resizeDim.w && m_resizeDim.h) { + reconfigParams.display_area.left = pVideoFormat->display_area.left; + reconfigParams.display_area.top = pVideoFormat->display_area.top; + reconfigParams.display_area.right = pVideoFormat->display_area.right; + reconfigParams.display_area.bottom = pVideoFormat->display_area.bottom; + m_nWidth = m_resizeDim.w; + m_nLumaHeight = m_resizeDim.h; + } + + if (m_cropRect.r && m_cropRect.b) { + reconfigParams.display_area.left = m_cropRect.l; + reconfigParams.display_area.top = m_cropRect.t; + reconfigParams.display_area.right = m_cropRect.r; + reconfigParams.display_area.bottom = m_cropRect.b; + m_nWidth = m_cropRect.r - m_cropRect.l; + m_nLumaHeight = m_cropRect.b - m_cropRect.t; + } + reconfigParams.ulTargetWidth = m_nWidth; + reconfigParams.ulTargetHeight = m_nLumaHeight; + } + + m_nChromaHeight = + (int)ceil(m_nLumaHeight * GetChromaHeightFactor(m_eOutputFormat)); + m_nNumChromaPlanes = GetChromaPlaneCount(m_eOutputFormat); + m_nSurfaceHeight = reconfigParams.ulTargetHeight; + m_nSurfaceWidth = reconfigParams.ulTargetWidth; + m_displayRect.b = reconfigParams.display_area.bottom; + m_displayRect.t = reconfigParams.display_area.top; + m_displayRect.l = reconfigParams.display_area.left; + m_displayRect.r = reconfigParams.display_area.right; + } + + reconfigParams.ulNumDecodeSurfaces = nDecodeSurface; + + START_TIMER + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + NVDEC_API_CALL(cuvidReconfigureDecoder(m_hDecoder, &reconfigParams)); + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + STOP_TIMER("Session Reconfigure Time: "); + + return nDecodeSurface; +} + +int NvDecoder::setReconfigParams(const Rect *pCropRect, const Dim *pResizeDim) { + m_bReconfigExternal = true; + m_bReconfigExtPPChange = false; + if (pCropRect) { + if (!((pCropRect->t == m_cropRect.t) && (pCropRect->l == m_cropRect.l) && + (pCropRect->b == m_cropRect.b) && (pCropRect->r == m_cropRect.r))) { + m_bReconfigExtPPChange = true; + m_cropRect = *pCropRect; + } + } + if (pResizeDim) { + if (!((pResizeDim->w == m_resizeDim.w) && + (pResizeDim->h == m_resizeDim.h))) { + m_bReconfigExtPPChange = true; + m_resizeDim = *pResizeDim; + } + } + + // Clear existing output buffers of different size + uint8_t *pFrame = NULL; + while (!m_vpFrame.empty()) { + pFrame = m_vpFrame.back(); + m_vpFrame.pop_back(); + if (m_bUseDeviceFrame) { + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + CUDA_DRVAPI_CALL(cuMemFree((CUdeviceptr)pFrame)); + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + } else { + delete pFrame; + } + } + + return 1; +} + +/* Return value from HandlePictureDecode() are interpreted as: + * 0: fail, >=1: succeeded + */ +int NvDecoder::HandlePictureDecode(CUVIDPICPARAMS *pPicParams) { + if (!m_hDecoder) { + NVDEC_THROW_ERROR("Decoder not initialized.", CUDA_ERROR_NOT_INITIALIZED); + return false; + } + m_nPicNumInDecodeOrder[pPicParams->CurrPicIdx] = m_nDecodePicCnt++; + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + NVDEC_API_CALL(cuvidDecodePicture(m_hDecoder, pPicParams)); + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + return 1; +} + +/* Return value from HandlePictureDisplay() are interpreted as: + * 0: fail, >=1: succeeded + */ +int NvDecoder::HandlePictureDisplay(CUVIDPARSERDISPINFO *pDispInfo) { + CUVIDPROCPARAMS videoProcessingParameters = {}; + videoProcessingParameters.progressive_frame = pDispInfo->progressive_frame; + videoProcessingParameters.second_field = pDispInfo->repeat_first_field + 1; + videoProcessingParameters.top_field_first = pDispInfo->top_field_first; + videoProcessingParameters.unpaired_field = pDispInfo->repeat_first_field < 0; + videoProcessingParameters.output_stream = m_cuvidStream; + + CUdeviceptr dpSrcFrame = 0; + unsigned int nSrcPitch = 0; + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + NVDEC_API_CALL(cuvidMapVideoFrame(m_hDecoder, pDispInfo->picture_index, + &dpSrcFrame, &nSrcPitch, + &videoProcessingParameters)); + + CUVIDGETDECODESTATUS DecodeStatus; + memset(&DecodeStatus, 0, sizeof(DecodeStatus)); + CUresult result = + cuvidGetDecodeStatus(m_hDecoder, pDispInfo->picture_index, &DecodeStatus); + if (result == CUDA_SUCCESS && + (DecodeStatus.decodeStatus == cuvidDecodeStatus_Error || + DecodeStatus.decodeStatus == cuvidDecodeStatus_Error_Concealed)) { + printf("Decode Error occurred for picture %d\n", + m_nPicNumInDecodeOrder[pDispInfo->picture_index]); + } + + uint8_t *pDecodedFrame = nullptr; + { + std::lock_guard lock(m_mtxVPFrame); + if ((unsigned)++m_nDecodedFrame > m_vpFrame.size()) { + // Not enough frames in stock + m_nFrameAlloc++; + uint8_t *pFrame = NULL; + if (m_bUseDeviceFrame) { + if (m_bDeviceFramePitched) { + CUDA_DRVAPI_CALL(cuMemAllocPitch( + (CUdeviceptr *)&pFrame, &m_nDeviceFramePitch, GetWidth() * m_nBPP, + m_nLumaHeight + (m_nChromaHeight * m_nNumChromaPlanes), 16)); + } else { + CUDA_DRVAPI_CALL(cuMemAlloc((CUdeviceptr *)&pFrame, GetFrameSize())); + } + } else { + pFrame = new uint8_t[GetFrameSize()]; + } + m_vpFrame.push_back(pFrame); + } + pDecodedFrame = m_vpFrame[m_nDecodedFrame - 1]; + } + + // Copy luma plane + CUDA_MEMCPY2D m = {0}; + m.srcMemoryType = CU_MEMORYTYPE_DEVICE; + m.srcDevice = dpSrcFrame; + m.srcPitch = nSrcPitch; + m.dstMemoryType = + m_bUseDeviceFrame ? CU_MEMORYTYPE_DEVICE : CU_MEMORYTYPE_HOST; + m.dstDevice = (CUdeviceptr)(m.dstHost = pDecodedFrame); + m.dstPitch = m_nDeviceFramePitch ? m_nDeviceFramePitch : GetWidth() * m_nBPP; + m.WidthInBytes = GetWidth() * m_nBPP; + m.Height = m_nLumaHeight; + CUDA_DRVAPI_CALL(cuMemcpy2DAsync(&m, m_cuvidStream)); + + // Copy chroma plane + // NVDEC output has luma height aligned by 2. Adjust chroma offset by aligning + // height + m.srcDevice = (CUdeviceptr)((uint8_t *)dpSrcFrame + + m.srcPitch * ((m_nSurfaceHeight + 1) & ~1)); + m.dstDevice = + (CUdeviceptr)(m.dstHost = pDecodedFrame + m.dstPitch * m_nLumaHeight); + m.Height = m_nChromaHeight; + CUDA_DRVAPI_CALL(cuMemcpy2DAsync(&m, m_cuvidStream)); + + if (m_nNumChromaPlanes == 2) { + m.srcDevice = (CUdeviceptr)((uint8_t *)dpSrcFrame + + m.srcPitch * ((m_nSurfaceHeight + 1) & ~1) * 2); + m.dstDevice = (CUdeviceptr)(m.dstHost = pDecodedFrame + + m.dstPitch * m_nLumaHeight * 2); + m.Height = m_nChromaHeight; + CUDA_DRVAPI_CALL(cuMemcpy2DAsync(&m, m_cuvidStream)); + } + CUDA_DRVAPI_CALL(cuStreamSynchronize(m_cuvidStream)); + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + + if ((int)m_vTimestamp.size() < m_nDecodedFrame) { + m_vTimestamp.resize(m_vpFrame.size()); + } + m_vTimestamp[m_nDecodedFrame - 1] = pDispInfo->timestamp; + + NVDEC_API_CALL(cuvidUnmapVideoFrame(m_hDecoder, dpSrcFrame)); + return 1; +} + +NvDecoder::NvDecoder(CUcontext cuContext, bool bUseDeviceFrame, + cudaVideoCodec eCodec, bool bLowLatency, + bool bDeviceFramePitched, const Rect *pCropRect, + const Dim *pResizeDim, int maxWidth, int maxHeight, + unsigned int clkRate) + : m_cuContext(cuContext), + m_bUseDeviceFrame(bUseDeviceFrame), + m_eCodec(eCodec), + m_bDeviceFramePitched(bDeviceFramePitched), + m_nMaxWidth(maxWidth), + m_nMaxHeight(maxHeight) { + if (pCropRect) m_cropRect = *pCropRect; + if (pResizeDim) m_resizeDim = *pResizeDim; + + NVDEC_API_CALL(cuvidCtxLockCreate(&m_ctxLock, cuContext)); + + CUVIDPARSERPARAMS videoParserParameters = {}; + videoParserParameters.CodecType = eCodec; + videoParserParameters.ulMaxNumDecodeSurfaces = 1; + videoParserParameters.ulClockRate = clkRate; + videoParserParameters.ulMaxDisplayDelay = bLowLatency ? 0 : 1; + videoParserParameters.pUserData = this; + videoParserParameters.pfnSequenceCallback = HandleVideoSequenceProc; + videoParserParameters.pfnDecodePicture = HandlePictureDecodeProc; + videoParserParameters.pfnDisplayPicture = HandlePictureDisplayProc; + videoParserParameters.pfnGetOperatingPoint = HandleOperatingPointProc; + NVDEC_API_CALL(cuvidCreateVideoParser(&m_hParser, &videoParserParameters)); +} + +NvDecoder::~NvDecoder() { + START_TIMER + + if (m_hParser) { + cuvidDestroyVideoParser(m_hParser); + } + cuCtxPushCurrent(m_cuContext); + if (m_hDecoder) { + cuvidDestroyDecoder(m_hDecoder); + } + + std::lock_guard lock(m_mtxVPFrame); + + for (uint8_t *pFrame : m_vpFrame) { + if (m_bUseDeviceFrame) { + cuMemFree((CUdeviceptr)pFrame); + } else { + delete[] pFrame; + } + } + cuCtxPopCurrent(NULL); + + cuvidCtxLockDestroy(m_ctxLock); + + STOP_TIMER("Session Deinitialization Time: "); +} + +int NvDecoder::Decode(const uint8_t *pData, int nSize, int nFlags, + int64_t nTimestamp) { + m_nDecodedFrame = 0; + m_nDecodedFrameReturned = 0; + CUVIDSOURCEDATAPACKET packet = {0}; + packet.payload = pData; + packet.payload_size = nSize; + packet.flags = nFlags | CUVID_PKT_TIMESTAMP; + packet.timestamp = nTimestamp; + if (!pData || nSize == 0) { + packet.flags |= CUVID_PKT_ENDOFSTREAM; + } + NVDEC_API_CALL(cuvidParseVideoData(m_hParser, &packet)); + m_cuvidStream = 0; + + return m_nDecodedFrame; +} + +uint8_t *NvDecoder::GetFrame(int64_t *pTimestamp) { + if (m_nDecodedFrame > 0) { + std::lock_guard lock(m_mtxVPFrame); + m_nDecodedFrame--; + if (pTimestamp) *pTimestamp = m_vTimestamp[m_nDecodedFrameReturned]; + return m_vpFrame[m_nDecodedFrameReturned++]; + } + + return NULL; +} + +uint8_t *NvDecoder::GetLockedFrame(int64_t *pTimestamp) { + uint8_t *pFrame; + uint64_t timestamp; + if (m_nDecodedFrame > 0) { + std::lock_guard lock(m_mtxVPFrame); + m_nDecodedFrame--; + pFrame = m_vpFrame[0]; + m_vpFrame.erase(m_vpFrame.begin(), m_vpFrame.begin() + 1); + + timestamp = m_vTimestamp[0]; + m_vTimestamp.erase(m_vTimestamp.begin(), m_vTimestamp.begin() + 1); + + if (pTimestamp) *pTimestamp = timestamp; + + return pFrame; + } + + return NULL; +} + +void NvDecoder::UnlockFrame(uint8_t **pFrame) { + std::lock_guard lock(m_mtxVPFrame); + m_vpFrame.insert(m_vpFrame.end(), &pFrame[0], &pFrame[1]); + + // add a dummy entry for timestamp + uint64_t timestamp[2] = {0}; + m_vTimestamp.insert(m_vTimestamp.end(), ×tamp[0], ×tamp[1]); +} diff --git a/src/media/video/decode/nvcodec/NvDecoder.h b/src/media/video/decode/nvcodec/NvDecoder.h new file mode 100644 index 0000000..ce67ba4 --- /dev/null +++ b/src/media/video/decode/nvcodec/NvDecoder.h @@ -0,0 +1,395 @@ +/* + * Copyright 2017-2020 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "Utils/NvCodecUtils.h" +#include "nvcuvid.h" + +/** + * @brief Exception class for error reporting from the decode API. + */ +class NVDECException : public std::exception { + public: + NVDECException(const std::string &errorStr, const CUresult errorCode) + : m_errorString(errorStr), m_errorCode(errorCode) {} + + virtual ~NVDECException() throw() {} + virtual const char *what() const throw() { return m_errorString.c_str(); } + CUresult getErrorCode() const { return m_errorCode; } + const std::string &getErrorString() const { return m_errorString; } + static NVDECException makeNVDECException(const std::string &errorStr, + const CUresult errorCode, + const std::string &functionName, + const std::string &fileName, + int lineNo); + + private: + std::string m_errorString; + CUresult m_errorCode; +}; + +inline NVDECException NVDECException::makeNVDECException( + const std::string &errorStr, const CUresult errorCode, + const std::string &functionName, const std::string &fileName, int lineNo) { + std::ostringstream errorLog; + errorLog << functionName << " : " << errorStr << " at " << fileName << ":" + << lineNo << std::endl; + NVDECException exception(errorLog.str(), errorCode); + return exception; +} + +#define NVDEC_THROW_ERROR(errorStr, errorCode) \ + do { \ + throw NVDECException::makeNVDECException( \ + errorStr, errorCode, __FUNCTION__, __FILE__, __LINE__); \ + } while (0) + +#define NVDEC_API_CALL(cuvidAPI) \ + do { \ + CUresult errorCode = cuvidAPI; \ + if (errorCode != CUDA_SUCCESS) { \ + std::ostringstream errorLog; \ + errorLog << #cuvidAPI << " returned error " << errorCode; \ + throw NVDECException::makeNVDECException( \ + errorLog.str(), errorCode, __FUNCTION__, __FILE__, __LINE__); \ + } \ + } while (0) + +struct Rect { + int l, t, r, b; +}; + +struct Dim { + int w, h; +}; + +/** + * @brief Base class for decoder interface. + */ +class NvDecoder { + public: + /** + * @brief This function is used to initialize the decoder session. + * Application must call this function to initialize the decoder, before + * starting to decode any frames. + */ + NvDecoder(CUcontext cuContext, bool bUseDeviceFrame, cudaVideoCodec eCodec, + bool bLowLatency = false, bool bDeviceFramePitched = false, + const Rect *pCropRect = NULL, const Dim *pResizeDim = NULL, + int maxWidth = 0, int maxHeight = 0, unsigned int clkRate = 1000); + ~NvDecoder(); + + /** + * @brief This function is used to get the current CUDA context. + */ + CUcontext GetContext() { return m_cuContext; } + + /** + * @brief This function is used to get the output frame width. + * NV12/P016 output format width is 2 byte aligned because of U and V + * interleave + */ + int GetWidth() { + assert(m_nWidth); + return (m_eOutputFormat == cudaVideoSurfaceFormat_NV12 || + m_eOutputFormat == cudaVideoSurfaceFormat_P016) + ? (m_nWidth + 1) & ~1 + : m_nWidth; + } + + /** + * @brief This function is used to get the actual decode width + */ + int GetDecodeWidth() { + assert(m_nWidth); + return m_nWidth; + } + + /** + * @brief This function is used to get the output frame height (Luma + * height). + */ + int GetHeight() { + assert(m_nLumaHeight); + return m_nLumaHeight; + } + + /** + * @brief This function is used to get the current chroma height. + */ + int GetChromaHeight() { + assert(m_nChromaHeight); + return m_nChromaHeight; + } + + /** + * @brief This function is used to get the number of chroma planes. + */ + int GetNumChromaPlanes() { + assert(m_nNumChromaPlanes); + return m_nNumChromaPlanes; + } + + /** + * @brief This function is used to get the current frame size based on + * pixel format. + */ + int GetFrameSize() { + assert(m_nWidth); + return GetWidth() * + (m_nLumaHeight + (m_nChromaHeight * m_nNumChromaPlanes)) * m_nBPP; + } + + /** + * @brief This function is used to get the current frame Luma plane size. + */ + int GetLumaPlaneSize() { + assert(m_nWidth); + return GetWidth() * m_nLumaHeight * m_nBPP; + } + + /** + * @brief This function is used to get the current frame chroma plane size. + */ + int GetChromaPlaneSize() { + assert(m_nWidth); + return GetWidth() * (m_nChromaHeight * m_nNumChromaPlanes) * m_nBPP; + } + + /** + * @brief This function is used to get the pitch of the device buffer + * holding the decoded frame. + */ + int GetDeviceFramePitch() { + assert(m_nWidth); + return m_nDeviceFramePitch ? (int)m_nDeviceFramePitch : GetWidth() * m_nBPP; + } + + /** + * @brief This function is used to get the bit depth associated with the + * pixel format. + */ + int GetBitDepth() { + assert(m_nWidth); + return m_nBitDepthMinus8 + 8; + } + + /** + * @brief This function is used to get the bytes used per pixel. + */ + int GetBPP() { + assert(m_nWidth); + return m_nBPP; + } + + /** + * @brief This function is used to get the YUV chroma format + */ + cudaVideoSurfaceFormat GetOutputFormat() { return m_eOutputFormat; } + + /** + * @brief This function is used to get information about the video stream + * (codec, display parameters etc) + */ + CUVIDEOFORMAT GetVideoFormatInfo() { + assert(m_nWidth); + return m_videoFormat; + } + + /** + * @brief This function is used to get codec string from codec id + */ + const char *GetCodecString(cudaVideoCodec eCodec); + + /** + * @brief This function is used to print information about the video stream + */ + std::string GetVideoInfo() const { return m_videoInfo.str(); } + + /** + * @brief This function decodes a frame and returns the number of frames + * that are available for display. All frames that are available for display + * should be read before making a subsequent decode call. + * @param pData - pointer to the data buffer that is to be decoded + * @param nSize - size of the data buffer in bytes + * @param nFlags - CUvideopacketflags for setting decode options + * @param nTimestamp - presentation timestamp + */ + int Decode(const uint8_t *pData, int nSize, int nFlags = 0, + int64_t nTimestamp = 0); + + /** + * @brief This function returns a decoded frame and timestamp. This + * function should be called in a loop for fetching all the frames that are + * available for display. + */ + uint8_t *GetFrame(int64_t *pTimestamp = nullptr); + + /** + * @brief This function decodes a frame and returns the locked frame + * buffers This makes the buffers available for use by the application without + * the buffers getting overwritten, even if subsequent decode calls are made. + * The frame buffers remain locked, until UnlockFrame() is called + */ + uint8_t *GetLockedFrame(int64_t *pTimestamp = nullptr); + + /** + * @brief This function unlocks the frame buffer and makes the frame + * buffers available for write again + * @param ppFrame - pointer to array of frames that are to be unlocked + * @param nFrame - number of frames to be unlocked + */ + void UnlockFrame(uint8_t **pFrame); + + /** + * @brief This function allows app to set decoder reconfig params + * @param pCropRect - cropping rectangle coordinates + * @param pResizeDim - width and height of resized output + */ + int setReconfigParams(const Rect *pCropRect, const Dim *pResizeDim); + + /** + * @brief This function allows app to set operating point for AV1 SVC clips + * @param opPoint - operating point of an AV1 scalable bitstream + * @param bDispAllLayers - Output all decoded frames of an AV1 scalable + * bitstream + */ + void SetOperatingPoint(const uint32_t opPoint, const bool bDispAllLayers) { + m_nOperatingPoint = opPoint; + m_bDispAllLayers = bDispAllLayers; + } + + // start a timer + void startTimer() { m_stDecode_time.Start(); } + + // stop the timer + double stopTimer() { return m_stDecode_time.Stop(); } + + private: + /** + * @brief Callback function to be registered for getting a callback when + * decoding of sequence starts + */ + static int CUDAAPI HandleVideoSequenceProc(void *pUserData, + CUVIDEOFORMAT *pVideoFormat) { + return ((NvDecoder *)pUserData)->HandleVideoSequence(pVideoFormat); + } + + /** + * @brief Callback function to be registered for getting a callback when a + * decoded frame is ready to be decoded + */ + static int CUDAAPI HandlePictureDecodeProc(void *pUserData, + CUVIDPICPARAMS *pPicParams) { + return ((NvDecoder *)pUserData)->HandlePictureDecode(pPicParams); + } + + /** + * @brief Callback function to be registered for getting a callback when a + * decoded frame is available for display + */ + static int CUDAAPI HandlePictureDisplayProc(void *pUserData, + CUVIDPARSERDISPINFO *pDispInfo) { + return ((NvDecoder *)pUserData)->HandlePictureDisplay(pDispInfo); + } + + /** + * @brief Callback function to be registered for getting a callback to get + * operating point when AV1 SVC sequence header start. + */ + static int CUDAAPI + HandleOperatingPointProc(void *pUserData, CUVIDOPERATINGPOINTINFO *pOPInfo) { + return ((NvDecoder *)pUserData)->GetOperatingPoint(pOPInfo); + } + + /** + * @brief This function gets called when a sequence is ready to be decoded. + The function also gets called when there is format change + */ + int HandleVideoSequence(CUVIDEOFORMAT *pVideoFormat); + + /** + * @brief This function gets called when a picture is ready to be decoded. + * cuvidDecodePicture is called from this function to decode the picture + */ + int HandlePictureDecode(CUVIDPICPARAMS *pPicParams); + + /** + * @brief This function gets called after a picture is decoded and available + for display. Frames are fetched and stored in internal buffer + */ + int HandlePictureDisplay(CUVIDPARSERDISPINFO *pDispInfo); + + /** + * @brief This function gets called when AV1 sequence encounter more than + * one operating points + */ + int GetOperatingPoint(CUVIDOPERATINGPOINTINFO *pOPInfo); + /** + * @brief This function reconfigure decoder if there is a change in + * sequence params. + */ + int ReconfigureDecoder(CUVIDEOFORMAT *pVideoFormat); + + private: + CUcontext m_cuContext = NULL; + CUvideoctxlock m_ctxLock; + CUvideoparser m_hParser = NULL; + CUvideodecoder m_hDecoder = NULL; + bool m_bUseDeviceFrame; + // dimension of the output + unsigned int m_nWidth = 0, m_nLumaHeight = 0, m_nChromaHeight = 0; + unsigned int m_nNumChromaPlanes = 0; + // height of the mapped surface + int m_nSurfaceHeight = 0; + int m_nSurfaceWidth = 0; + cudaVideoCodec m_eCodec = cudaVideoCodec_NumCodecs; + cudaVideoChromaFormat m_eChromaFormat = cudaVideoChromaFormat_420; + cudaVideoSurfaceFormat m_eOutputFormat = cudaVideoSurfaceFormat_NV12; + int m_nBitDepthMinus8 = 0; + int m_nBPP = 1; + CUVIDEOFORMAT m_videoFormat = {}; + Rect m_displayRect = {}; + // stock of frames + std::vector m_vpFrame; + // timestamps of decoded frames + std::vector m_vTimestamp; + int m_nDecodedFrame = 0, m_nDecodedFrameReturned = 0; + int m_nDecodePicCnt = 0, m_nPicNumInDecodeOrder[32]; + bool m_bEndDecodeDone = false; + std::mutex m_mtxVPFrame; + int m_nFrameAlloc = 0; + CUstream m_cuvidStream = 0; + bool m_bDeviceFramePitched = false; + size_t m_nDeviceFramePitch = 0; + Rect m_cropRect = {}; + Dim m_resizeDim = {}; + + std::ostringstream m_videoInfo; + unsigned int m_nMaxWidth = 0, m_nMaxHeight = 0; + bool m_bReconfigExternal = false; + bool m_bReconfigExtPPChange = false; + StopWatch m_stDecode_time; + + unsigned int m_nOperatingPoint = 0; + bool m_bDispAllLayers = false; +}; diff --git a/src/media/video/decode/nvcodec/nv_decoder.cpp b/src/media/video/decode/nvcodec/nv_decoder.cpp new file mode 100644 index 0000000..d2a2ba9 --- /dev/null +++ b/src/media/video/decode/nvcodec/nv_decoder.cpp @@ -0,0 +1,65 @@ +#include "nv_decoder.h" + +#include "log.h" + +VideoDecoder::VideoDecoder() {} +VideoDecoder::~VideoDecoder() {} + +int VideoDecoder::Init() { + ck(cuInit(0)); + int nGpu = 0; + int iGpu = 0; + + ck(cuDeviceGetCount(&nGpu)); + if (nGpu < 1) { + return -1; + } + + CUdevice cuDevice; + cuDeviceGet(&cuDevice, iGpu); + + CUcontext cuContext = NULL; + cuCtxCreate(&cuContext, 0, cuDevice); + if (!cuContext) { + return -1; + } + + decoder = new NvDecoder(cuContext, false, cudaVideoCodec_H264, true); + return 0; +} + +int VideoDecoder::Decode(const uint8_t *pData, int nSize) { + if (!decoder) { + return -1; + } + + if ((*(pData + 4) & 0x1f) == 0x07) { + // LOG_WARN("Receive key frame"); + } + + int ret = decoder->Decode(pData, nSize); + return ret; +} + +int VideoDecoder::GetFrame(uint8_t *yuv_data, uint32_t &width, uint32_t &height, + uint32_t &size) { + if (nullptr == decoder) { + return -1; + } + cudaVideoSurfaceFormat format = decoder->GetOutputFormat(); + if (format == cudaVideoSurfaceFormat_NV12) { + uint8_t *data = nullptr; + data = decoder->GetFrame(); + if (data) { + yuv_data = data; + width = decoder->GetWidth(); + height = decoder->GetHeight(); + size = width * height * 3 / 2; + return 0; + + return -1; + } + return -1; + } + return -1; +} diff --git a/src/media/video/decode/nvcodec/nv_decoder.h b/src/media/video/decode/nvcodec/nv_decoder.h new file mode 100644 index 0000000..20e7ecc --- /dev/null +++ b/src/media/video/decode/nvcodec/nv_decoder.h @@ -0,0 +1,21 @@ +#ifndef _NV_DECODER_H_ +#define _NV_DECODER_H_ + +#include "NvDecoder.h" + +class VideoDecoder { + public: + VideoDecoder(); + ~VideoDecoder(); + + int Init(); + int Decode(const uint8_t* pData, int nSize); + int GetFrame(uint8_t* yuv_data, uint32_t& width, uint32_t& height, + uint32_t& size); + + NvDecoder* decoder = nullptr; + bool get_first_keyframe_ = false; + bool skip_frame_ = false; +}; + +#endif \ No newline at end of file diff --git a/src/media/video/encode/nvcodec/NvEncoder.cpp b/src/media/video/encode/nvcodec/NvEncoder.cpp new file mode 100644 index 0000000..cf68141 --- /dev/null +++ b/src/media/video/encode/nvcodec/NvEncoder.cpp @@ -0,0 +1,909 @@ +/* + * Copyright 2017-2020 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +#include "NvEncoder.h" + +#ifndef _WIN32 +#include +static inline bool operator==(const GUID &guid1, const GUID &guid2) { + return !memcmp(&guid1, &guid2, sizeof(GUID)); +} + +static inline bool operator!=(const GUID &guid1, const GUID &guid2) { + return !(guid1 == guid2); +} +#endif + +NvEncoder::NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, void *pDevice, + uint32_t nWidth, uint32_t nHeight, + NV_ENC_BUFFER_FORMAT eBufferFormat, + uint32_t nExtraOutputDelay, bool bMotionEstimationOnly, + bool bOutputInVideoMemory) + : m_pDevice(pDevice), + m_eDeviceType(eDeviceType), + m_nWidth(nWidth), + m_nHeight(nHeight), + m_nMaxEncodeWidth(nWidth), + m_nMaxEncodeHeight(nHeight), + m_eBufferFormat(eBufferFormat), + m_bMotionEstimationOnly(bMotionEstimationOnly), + m_bOutputInVideoMemory(bOutputInVideoMemory), + m_nExtraOutputDelay(nExtraOutputDelay), + m_hEncoder(nullptr) { + LoadNvEncApi(); + + if (!m_nvenc.nvEncOpenEncodeSession) { + m_nEncoderBuffer = 0; + NVENC_THROW_ERROR("EncodeAPI not found", NV_ENC_ERR_NO_ENCODE_DEVICE); + } + + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS encodeSessionExParams = { + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER}; + encodeSessionExParams.device = m_pDevice; + encodeSessionExParams.deviceType = m_eDeviceType; + encodeSessionExParams.apiVersion = NVENCAPI_VERSION; + void *hEncoder = NULL; + NVENC_API_CALL( + m_nvenc.nvEncOpenEncodeSessionEx(&encodeSessionExParams, &hEncoder)); + m_hEncoder = hEncoder; +} + +void NvEncoder::LoadNvEncApi() { + uint32_t version = 0; + uint32_t currentVersion = + (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION; + NVENC_API_CALL(NvEncodeAPIGetMaxSupportedVersion(&version)); + if (currentVersion > version) { + NVENC_THROW_ERROR( + "Current Driver Version does not support this NvEncodeAPI version, " + "please upgrade driver", + NV_ENC_ERR_INVALID_VERSION); + } + + m_nvenc = {NV_ENCODE_API_FUNCTION_LIST_VER}; + NVENC_API_CALL(NvEncodeAPICreateInstance(&m_nvenc)); +} + +NvEncoder::~NvEncoder() { DestroyHWEncoder(); } + +void NvEncoder::CreateDefaultEncoderParams( + NV_ENC_INITIALIZE_PARAMS *pIntializeParams, GUID codecGuid, GUID presetGuid, + NV_ENC_TUNING_INFO tuningInfo) { + if (!m_hEncoder) { + NVENC_THROW_ERROR("Encoder Initialization failed", + NV_ENC_ERR_NO_ENCODE_DEVICE); + return; + } + + if (pIntializeParams == nullptr || + pIntializeParams->encodeConfig == nullptr) { + NVENC_THROW_ERROR( + "pInitializeParams and pInitializeParams->encodeConfig can't be NULL", + NV_ENC_ERR_INVALID_PTR); + } + + memset(pIntializeParams->encodeConfig, 0, sizeof(NV_ENC_CONFIG)); + auto pEncodeConfig = pIntializeParams->encodeConfig; + memset(pIntializeParams, 0, sizeof(NV_ENC_INITIALIZE_PARAMS)); + pIntializeParams->encodeConfig = pEncodeConfig; + + pIntializeParams->encodeConfig->version = NV_ENC_CONFIG_VER; + pIntializeParams->version = NV_ENC_INITIALIZE_PARAMS_VER; + + pIntializeParams->encodeGUID = codecGuid; + pIntializeParams->presetGUID = presetGuid; + pIntializeParams->encodeWidth = m_nWidth; + pIntializeParams->encodeHeight = m_nHeight; + pIntializeParams->darWidth = m_nWidth; + pIntializeParams->darHeight = m_nHeight; + pIntializeParams->frameRateNum = 30; + pIntializeParams->frameRateDen = 1; + pIntializeParams->enablePTD = 1; + pIntializeParams->reportSliceOffsets = 0; + pIntializeParams->enableSubFrameWrite = 0; + pIntializeParams->maxEncodeWidth = m_nWidth; + pIntializeParams->maxEncodeHeight = m_nHeight; + pIntializeParams->enableMEOnlyMode = m_bMotionEstimationOnly; + pIntializeParams->enableOutputInVidmem = m_bOutputInVideoMemory; +#if defined(_WIN32) + if (!m_bOutputInVideoMemory) { + pIntializeParams->enableEncodeAsync = + GetCapabilityValue(codecGuid, NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT); + } +#endif + + NV_ENC_PRESET_CONFIG presetConfig = {NV_ENC_PRESET_CONFIG_VER, + {NV_ENC_CONFIG_VER}}; + m_nvenc.nvEncGetEncodePresetConfig(m_hEncoder, codecGuid, presetGuid, + &presetConfig); + memcpy(pIntializeParams->encodeConfig, &presetConfig.presetCfg, + sizeof(NV_ENC_CONFIG)); + pIntializeParams->encodeConfig->frameIntervalP = 1; + pIntializeParams->encodeConfig->gopLength = NVENC_INFINITE_GOPLENGTH; + + pIntializeParams->encodeConfig->rcParams.rateControlMode = + NV_ENC_PARAMS_RC_CONSTQP; + + if (!m_bMotionEstimationOnly) { + pIntializeParams->tuningInfo = tuningInfo; + NV_ENC_PRESET_CONFIG presetConfig = {NV_ENC_PRESET_CONFIG_VER, + {NV_ENC_CONFIG_VER}}; + m_nvenc.nvEncGetEncodePresetConfigEx(m_hEncoder, codecGuid, presetGuid, + tuningInfo, &presetConfig); + memcpy(pIntializeParams->encodeConfig, &presetConfig.presetCfg, + sizeof(NV_ENC_CONFIG)); + } else { + m_encodeConfig.version = NV_ENC_CONFIG_VER; + m_encodeConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP; + m_encodeConfig.rcParams.constQP = {28, 31, 25}; + } + + if (pIntializeParams->encodeGUID == NV_ENC_CODEC_H264_GUID) { + if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) { + pIntializeParams->encodeConfig->encodeCodecConfig.h264Config + .chromaFormatIDC = 3; + } + pIntializeParams->encodeConfig->encodeCodecConfig.h264Config.idrPeriod = + pIntializeParams->encodeConfig->gopLength; + } else if (pIntializeParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) { + pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig + .pixelBitDepthMinus8 = + (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) + ? 2 + : 0; + if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) { + pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig + .chromaFormatIDC = 3; + } + pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig.idrPeriod = + pIntializeParams->encodeConfig->gopLength; + } + + return; +} + +void NvEncoder::CreateEncoder(const NV_ENC_INITIALIZE_PARAMS *pEncoderParams) { + if (!m_hEncoder) { + NVENC_THROW_ERROR("Encoder Initialization failed", + NV_ENC_ERR_NO_ENCODE_DEVICE); + } + + if (!pEncoderParams) { + NVENC_THROW_ERROR("Invalid NV_ENC_INITIALIZE_PARAMS ptr", + NV_ENC_ERR_INVALID_PTR); + } + + if (pEncoderParams->encodeWidth == 0 || pEncoderParams->encodeHeight == 0) { + NVENC_THROW_ERROR("Invalid encoder width and height", + NV_ENC_ERR_INVALID_PARAM); + } + + if (pEncoderParams->encodeGUID != NV_ENC_CODEC_H264_GUID && + pEncoderParams->encodeGUID != NV_ENC_CODEC_HEVC_GUID) { + NVENC_THROW_ERROR("Invalid codec guid", NV_ENC_ERR_INVALID_PARAM); + } + + if (pEncoderParams->encodeGUID == NV_ENC_CODEC_H264_GUID) { + if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) { + NVENC_THROW_ERROR("10-bit format isn't supported by H264 encoder", + NV_ENC_ERR_INVALID_PARAM); + } + } + + // set other necessary params if not set yet + if (pEncoderParams->encodeGUID == NV_ENC_CODEC_H264_GUID) { + if ((m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444) && + (pEncoderParams->encodeConfig->encodeCodecConfig.h264Config + .chromaFormatIDC != 3)) { + NVENC_THROW_ERROR("Invalid ChromaFormatIDC", NV_ENC_ERR_INVALID_PARAM); + } + } + + if (pEncoderParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) { + bool yuv10BitFormat = + (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) + ? true + : false; + if (yuv10BitFormat && pEncoderParams->encodeConfig->encodeCodecConfig + .hevcConfig.pixelBitDepthMinus8 != 2) { + NVENC_THROW_ERROR("Invalid PixelBitdepth", NV_ENC_ERR_INVALID_PARAM); + } + + if ((m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) && + (pEncoderParams->encodeConfig->encodeCodecConfig.hevcConfig + .chromaFormatIDC != 3)) { + NVENC_THROW_ERROR("Invalid ChromaFormatIDC", NV_ENC_ERR_INVALID_PARAM); + } + } + + memcpy(&m_initializeParams, pEncoderParams, sizeof(m_initializeParams)); + m_initializeParams.version = NV_ENC_INITIALIZE_PARAMS_VER; + + if (pEncoderParams->encodeConfig) { + memcpy(&m_encodeConfig, pEncoderParams->encodeConfig, + sizeof(m_encodeConfig)); + m_encodeConfig.version = NV_ENC_CONFIG_VER; + } else { + NV_ENC_PRESET_CONFIG presetConfig = {NV_ENC_PRESET_CONFIG_VER, + {NV_ENC_CONFIG_VER}}; + if (!m_bMotionEstimationOnly) { + m_nvenc.nvEncGetEncodePresetConfigEx( + m_hEncoder, pEncoderParams->encodeGUID, pEncoderParams->presetGUID, + pEncoderParams->tuningInfo, &presetConfig); + memcpy(&m_encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG)); + } else { + m_encodeConfig.version = NV_ENC_CONFIG_VER; + m_encodeConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP; + m_encodeConfig.rcParams.constQP = {28, 31, 25}; + } + } + m_initializeParams.encodeConfig = &m_encodeConfig; + + NVENC_API_CALL( + m_nvenc.nvEncInitializeEncoder(m_hEncoder, &m_initializeParams)); + + m_bEncoderInitialized = true; + m_nWidth = m_initializeParams.encodeWidth; + m_nHeight = m_initializeParams.encodeHeight; + m_nMaxEncodeWidth = m_initializeParams.maxEncodeWidth; + m_nMaxEncodeHeight = m_initializeParams.maxEncodeHeight; + + m_nEncoderBuffer = m_encodeConfig.frameIntervalP + + m_encodeConfig.rcParams.lookaheadDepth + + m_nExtraOutputDelay; + m_nOutputDelay = m_nEncoderBuffer - 1; + m_vMappedInputBuffers.resize(m_nEncoderBuffer, nullptr); + + if (!m_bOutputInVideoMemory) { + m_vpCompletionEvent.resize(m_nEncoderBuffer, nullptr); + } + +#if defined(_WIN32) + for (uint32_t i = 0; i < m_vpCompletionEvent.size(); i++) { + m_vpCompletionEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL); + NV_ENC_EVENT_PARAMS eventParams = {NV_ENC_EVENT_PARAMS_VER}; + eventParams.completionEvent = m_vpCompletionEvent[i]; + m_nvenc.nvEncRegisterAsyncEvent(m_hEncoder, &eventParams); + } +#endif + + if (m_bMotionEstimationOnly) { + m_vMappedRefBuffers.resize(m_nEncoderBuffer, nullptr); + + if (!m_bOutputInVideoMemory) { + InitializeMVOutputBuffer(); + } + } else { + if (!m_bOutputInVideoMemory) { + m_vBitstreamOutputBuffer.resize(m_nEncoderBuffer, nullptr); + InitializeBitstreamBuffer(); + } + } + + AllocateInputBuffers(m_nEncoderBuffer); +} + +void NvEncoder::DestroyEncoder() { + if (!m_hEncoder) { + return; + } + + ReleaseInputBuffers(); + + DestroyHWEncoder(); +} + +void NvEncoder::DestroyHWEncoder() { + if (!m_hEncoder) { + return; + } + +#if defined(_WIN32) + for (uint32_t i = 0; i < m_vpCompletionEvent.size(); i++) { + if (m_vpCompletionEvent[i]) { + NV_ENC_EVENT_PARAMS eventParams = {NV_ENC_EVENT_PARAMS_VER}; + eventParams.completionEvent = m_vpCompletionEvent[i]; + m_nvenc.nvEncUnregisterAsyncEvent(m_hEncoder, &eventParams); + CloseHandle(m_vpCompletionEvent[i]); + } + } + m_vpCompletionEvent.clear(); +#endif + + if (m_bMotionEstimationOnly) { + DestroyMVOutputBuffer(); + } else { + DestroyBitstreamBuffer(); + } + + m_nvenc.nvEncDestroyEncoder(m_hEncoder); + + m_hEncoder = nullptr; + + m_bEncoderInitialized = false; +} + +const NvEncInputFrame *NvEncoder::GetNextInputFrame() { + int i = m_iToSend % m_nEncoderBuffer; + return &m_vInputFrames[i]; +} + +const NvEncInputFrame *NvEncoder::GetNextReferenceFrame() { + int i = m_iToSend % m_nEncoderBuffer; + return &m_vReferenceFrames[i]; +} + +void NvEncoder::MapResources(uint32_t bfrIdx) { + NV_ENC_MAP_INPUT_RESOURCE mapInputResource = {NV_ENC_MAP_INPUT_RESOURCE_VER}; + + mapInputResource.registeredResource = m_vRegisteredResources[bfrIdx]; + NVENC_API_CALL(m_nvenc.nvEncMapInputResource(m_hEncoder, &mapInputResource)); + m_vMappedInputBuffers[bfrIdx] = mapInputResource.mappedResource; + + if (m_bMotionEstimationOnly) { + mapInputResource.registeredResource = + m_vRegisteredResourcesForReference[bfrIdx]; + NVENC_API_CALL( + m_nvenc.nvEncMapInputResource(m_hEncoder, &mapInputResource)); + m_vMappedRefBuffers[bfrIdx] = mapInputResource.mappedResource; + } +} + +void NvEncoder::EncodeFrame(std::vector> &vPacket, + NV_ENC_PIC_PARAMS *pPicParams) { + vPacket.clear(); + if (!IsHWEncoderInitialized()) { + NVENC_THROW_ERROR("Encoder device not found", NV_ENC_ERR_NO_ENCODE_DEVICE); + } + + int bfrIdx = m_iToSend % m_nEncoderBuffer; + + MapResources(bfrIdx); + + NVENCSTATUS nvStatus = DoEncode(m_vMappedInputBuffers[bfrIdx], + m_vBitstreamOutputBuffer[bfrIdx], pPicParams); + + if (nvStatus == NV_ENC_SUCCESS || nvStatus == NV_ENC_ERR_NEED_MORE_INPUT) { + m_iToSend++; + GetEncodedPacket(m_vBitstreamOutputBuffer, vPacket, true); + } else { + NVENC_THROW_ERROR("nvEncEncodePicture API failed", nvStatus); + } +} + +void NvEncoder::RunMotionEstimation(std::vector &mvData) { + if (!m_hEncoder) { + NVENC_THROW_ERROR("Encoder Initialization failed", + NV_ENC_ERR_NO_ENCODE_DEVICE); + return; + } + + const uint32_t bfrIdx = m_iToSend % m_nEncoderBuffer; + + MapResources(bfrIdx); + + NVENCSTATUS nvStatus = DoMotionEstimation(m_vMappedInputBuffers[bfrIdx], + m_vMappedRefBuffers[bfrIdx], + m_vMVDataOutputBuffer[bfrIdx]); + + if (nvStatus == NV_ENC_SUCCESS) { + m_iToSend++; + std::vector> vPacket; + GetEncodedPacket(m_vMVDataOutputBuffer, vPacket, true); + if (vPacket.size() != 1) { + NVENC_THROW_ERROR( + "GetEncodedPacket() doesn't return one (and only one) MVData", + NV_ENC_ERR_GENERIC); + } + mvData = vPacket[0]; + } else { + NVENC_THROW_ERROR("nvEncEncodePicture API failed", nvStatus); + } +} + +void NvEncoder::GetSequenceParams(std::vector &seqParams) { + uint8_t spsppsData[1024]; // Assume maximum spspps data is 1KB or less + memset(spsppsData, 0, sizeof(spsppsData)); + NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = {NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER}; + uint32_t spsppsSize = 0; + + payload.spsppsBuffer = spsppsData; + payload.inBufferSize = sizeof(spsppsData); + payload.outSPSPPSPayloadSize = &spsppsSize; + NVENC_API_CALL(m_nvenc.nvEncGetSequenceParams(m_hEncoder, &payload)); + seqParams.clear(); + seqParams.insert(seqParams.end(), &spsppsData[0], &spsppsData[spsppsSize]); +} + +NVENCSTATUS NvEncoder::DoEncode(NV_ENC_INPUT_PTR inputBuffer, + NV_ENC_OUTPUT_PTR outputBuffer, + NV_ENC_PIC_PARAMS *pPicParams) { + NV_ENC_PIC_PARAMS picParams = {}; + if (pPicParams) { + picParams = *pPicParams; + } + picParams.version = NV_ENC_PIC_PARAMS_VER; + picParams.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; + picParams.inputBuffer = inputBuffer; + picParams.bufferFmt = GetPixelFormat(); + picParams.inputWidth = GetEncodeWidth(); + picParams.inputHeight = GetEncodeHeight(); + picParams.outputBitstream = outputBuffer; + picParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer); + NVENCSTATUS nvStatus = m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams); + + return nvStatus; +} + +void NvEncoder::SendEOS() { + NV_ENC_PIC_PARAMS picParams = {NV_ENC_PIC_PARAMS_VER}; + picParams.encodePicFlags = NV_ENC_PIC_FLAG_EOS; + picParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer); + NVENC_API_CALL(m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams)); +} + +void NvEncoder::EndEncode(std::vector> &vPacket) { + vPacket.clear(); + if (!IsHWEncoderInitialized()) { + NVENC_THROW_ERROR("Encoder device not initialized", + NV_ENC_ERR_ENCODER_NOT_INITIALIZED); + } + + SendEOS(); + + GetEncodedPacket(m_vBitstreamOutputBuffer, vPacket, false); +} + +void NvEncoder::GetEncodedPacket(std::vector &vOutputBuffer, + std::vector> &vPacket, + bool bOutputDelay) { + unsigned i = 0; + int iEnd = bOutputDelay ? m_iToSend - m_nOutputDelay : m_iToSend; + for (; m_iGot < iEnd; m_iGot++) { + WaitForCompletionEvent(m_iGot % m_nEncoderBuffer); + NV_ENC_LOCK_BITSTREAM lockBitstreamData = {NV_ENC_LOCK_BITSTREAM_VER}; + lockBitstreamData.outputBitstream = + vOutputBuffer[m_iGot % m_nEncoderBuffer]; + lockBitstreamData.doNotWait = false; + NVENC_API_CALL(m_nvenc.nvEncLockBitstream(m_hEncoder, &lockBitstreamData)); + + uint8_t *pData = (uint8_t *)lockBitstreamData.bitstreamBufferPtr; + if (vPacket.size() < i + 1) { + vPacket.push_back(std::vector()); + } + vPacket[i].clear(); + vPacket[i].insert(vPacket[i].end(), &pData[0], + &pData[lockBitstreamData.bitstreamSizeInBytes]); + i++; + + NVENC_API_CALL(m_nvenc.nvEncUnlockBitstream( + m_hEncoder, lockBitstreamData.outputBitstream)); + + if (m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer]) { + NVENC_API_CALL(m_nvenc.nvEncUnmapInputResource( + m_hEncoder, m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer])); + m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer] = nullptr; + } + + if (m_bMotionEstimationOnly && + m_vMappedRefBuffers[m_iGot % m_nEncoderBuffer]) { + NVENC_API_CALL(m_nvenc.nvEncUnmapInputResource( + m_hEncoder, m_vMappedRefBuffers[m_iGot % m_nEncoderBuffer])); + m_vMappedRefBuffers[m_iGot % m_nEncoderBuffer] = nullptr; + } + } +} + +bool NvEncoder::Reconfigure( + const NV_ENC_RECONFIGURE_PARAMS *pReconfigureParams) { + NVENC_API_CALL(m_nvenc.nvEncReconfigureEncoder( + m_hEncoder, const_cast(pReconfigureParams))); + + memcpy(&m_initializeParams, &(pReconfigureParams->reInitEncodeParams), + sizeof(m_initializeParams)); + if (pReconfigureParams->reInitEncodeParams.encodeConfig) { + memcpy(&m_encodeConfig, pReconfigureParams->reInitEncodeParams.encodeConfig, + sizeof(m_encodeConfig)); + } + + m_nWidth = m_initializeParams.encodeWidth; + m_nHeight = m_initializeParams.encodeHeight; + m_nMaxEncodeWidth = m_initializeParams.maxEncodeWidth; + m_nMaxEncodeHeight = m_initializeParams.maxEncodeHeight; + + return true; +} + +NV_ENC_REGISTERED_PTR NvEncoder::RegisterResource( + void *pBuffer, NV_ENC_INPUT_RESOURCE_TYPE eResourceType, int width, + int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, + NV_ENC_BUFFER_USAGE bufferUsage) { + NV_ENC_REGISTER_RESOURCE registerResource = {NV_ENC_REGISTER_RESOURCE_VER}; + registerResource.resourceType = eResourceType; + registerResource.resourceToRegister = pBuffer; + registerResource.width = width; + registerResource.height = height; + registerResource.pitch = pitch; + registerResource.bufferFormat = bufferFormat; + registerResource.bufferUsage = bufferUsage; + NVENC_API_CALL(m_nvenc.nvEncRegisterResource(m_hEncoder, ®isterResource)); + + return registerResource.registeredResource; +} + +void NvEncoder::RegisterInputResources(std::vector inputframes, + NV_ENC_INPUT_RESOURCE_TYPE eResourceType, + int width, int height, int pitch, + NV_ENC_BUFFER_FORMAT bufferFormat, + bool bReferenceFrame) { + for (uint32_t i = 0; i < inputframes.size(); ++i) { + NV_ENC_REGISTERED_PTR registeredPtr = + RegisterResource(inputframes[i], eResourceType, width, height, pitch, + bufferFormat, NV_ENC_INPUT_IMAGE); + + std::vector _chromaOffsets; + NvEncoder::GetChromaSubPlaneOffsets(bufferFormat, pitch, height, + _chromaOffsets); + NvEncInputFrame inputframe = {}; + inputframe.inputPtr = (void *)inputframes[i]; + inputframe.chromaOffsets[0] = 0; + inputframe.chromaOffsets[1] = 0; + for (uint32_t ch = 0; ch < _chromaOffsets.size(); ch++) { + inputframe.chromaOffsets[ch] = _chromaOffsets[ch]; + } + inputframe.numChromaPlanes = NvEncoder::GetNumChromaPlanes(bufferFormat); + inputframe.pitch = pitch; + inputframe.chromaPitch = NvEncoder::GetChromaPitch(bufferFormat, pitch); + inputframe.bufferFormat = bufferFormat; + inputframe.resourceType = eResourceType; + + if (bReferenceFrame) { + m_vRegisteredResourcesForReference.push_back(registeredPtr); + m_vReferenceFrames.push_back(inputframe); + } else { + m_vRegisteredResources.push_back(registeredPtr); + m_vInputFrames.push_back(inputframe); + } + } +} + +void NvEncoder::FlushEncoder() { + if (!m_bMotionEstimationOnly && !m_bOutputInVideoMemory) { + // Incase of error it is possible for buffers still mapped to encoder. + // flush the encoder queue and then unmapped it if any surface is still + // mapped + try { + std::vector> vPacket; + EndEncode(vPacket); + } catch (...) { + } + } +} + +void NvEncoder::UnregisterInputResources() { + FlushEncoder(); + + if (m_bMotionEstimationOnly) { + for (uint32_t i = 0; i < m_vMappedRefBuffers.size(); ++i) { + if (m_vMappedRefBuffers[i]) { + m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedRefBuffers[i]); + } + } + } + m_vMappedRefBuffers.clear(); + + for (uint32_t i = 0; i < m_vMappedInputBuffers.size(); ++i) { + if (m_vMappedInputBuffers[i]) { + m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedInputBuffers[i]); + } + } + m_vMappedInputBuffers.clear(); + + for (uint32_t i = 0; i < m_vRegisteredResources.size(); ++i) { + if (m_vRegisteredResources[i]) { + m_nvenc.nvEncUnregisterResource(m_hEncoder, m_vRegisteredResources[i]); + } + } + m_vRegisteredResources.clear(); + + for (uint32_t i = 0; i < m_vRegisteredResourcesForReference.size(); ++i) { + if (m_vRegisteredResourcesForReference[i]) { + m_nvenc.nvEncUnregisterResource(m_hEncoder, + m_vRegisteredResourcesForReference[i]); + } + } + m_vRegisteredResourcesForReference.clear(); +} + +void NvEncoder::WaitForCompletionEvent(int iEvent) { +#if defined(_WIN32) + // Check if we are in async mode. If not, don't wait for event; + NV_ENC_CONFIG sEncodeConfig = {0}; + NV_ENC_INITIALIZE_PARAMS sInitializeParams = {0}; + sInitializeParams.encodeConfig = &sEncodeConfig; + GetInitializeParams(&sInitializeParams); + + if (0U == sInitializeParams.enableEncodeAsync) { + return; + } +#ifdef DEBUG + WaitForSingleObject(m_vpCompletionEvent[iEvent], INFINITE); +#else + // wait for 20s which is infinite on terms of gpu time + if (WaitForSingleObject(m_vpCompletionEvent[iEvent], 20000) == WAIT_FAILED) { + NVENC_THROW_ERROR("Failed to encode frame", NV_ENC_ERR_GENERIC); + } +#endif +#endif +} + +uint32_t NvEncoder::GetWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t width) { + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_YUV444: + return width; + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return width * 2; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return width * 4; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + +uint32_t NvEncoder::GetNumChromaPlanes( + const NV_ENC_BUFFER_FORMAT bufferFormat) { + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return 1; + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return 2; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return -1; + } +} + +uint32_t NvEncoder::GetChromaPitch(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t lumaPitch) { + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return lumaPitch; + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + return (lumaPitch + 1) / 2; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return -1; + } +} + +void NvEncoder::GetChromaSubPlaneOffsets( + const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t pitch, + const uint32_t height, std::vector &chromaOffsets) { + chromaOffsets.clear(); + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + chromaOffsets.push_back(pitch * height); + return; + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + chromaOffsets.push_back(pitch * height); + chromaOffsets.push_back(chromaOffsets[0] + + (NvEncoder::GetChromaPitch(bufferFormat, pitch) * + GetChromaHeight(bufferFormat, height))); + return; + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + chromaOffsets.push_back(pitch * height); + chromaOffsets.push_back(chromaOffsets[0] + (pitch * height)); + return; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return; + } +} + +uint32_t NvEncoder::GetChromaHeight(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t lumaHeight) { + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return (lumaHeight + 1) / 2; + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return lumaHeight; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + +uint32_t NvEncoder::GetChromaWidthInBytes( + const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaWidth) { + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + return (lumaWidth + 1) / 2; + case NV_ENC_BUFFER_FORMAT_NV12: + return lumaWidth; + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return 2 * lumaWidth; + case NV_ENC_BUFFER_FORMAT_YUV444: + return lumaWidth; + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return 2 * lumaWidth; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + +int NvEncoder::GetCapabilityValue(GUID guidCodec, NV_ENC_CAPS capsToQuery) { + if (!m_hEncoder) { + return 0; + } + NV_ENC_CAPS_PARAM capsParam = {NV_ENC_CAPS_PARAM_VER}; + capsParam.capsToQuery = capsToQuery; + int v; + m_nvenc.nvEncGetEncodeCaps(m_hEncoder, guidCodec, &capsParam, &v); + return v; +} + +int NvEncoder::GetFrameSize() const { + switch (GetPixelFormat()) { + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_NV12: + return GetEncodeWidth() * + (GetEncodeHeight() + (GetEncodeHeight() + 1) / 2); + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return 2 * GetEncodeWidth() * + (GetEncodeHeight() + (GetEncodeHeight() + 1) / 2); + case NV_ENC_BUFFER_FORMAT_YUV444: + return GetEncodeWidth() * GetEncodeHeight() * 3; + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return 2 * GetEncodeWidth() * GetEncodeHeight() * 3; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 4 * GetEncodeWidth() * GetEncodeHeight(); + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + +void NvEncoder::GetInitializeParams( + NV_ENC_INITIALIZE_PARAMS *pInitializeParams) { + if (!pInitializeParams || !pInitializeParams->encodeConfig) { + NVENC_THROW_ERROR( + "Both pInitializeParams and pInitializeParams->encodeConfig can't be " + "NULL", + NV_ENC_ERR_INVALID_PTR); + } + NV_ENC_CONFIG *pEncodeConfig = pInitializeParams->encodeConfig; + *pEncodeConfig = m_encodeConfig; + *pInitializeParams = m_initializeParams; + pInitializeParams->encodeConfig = pEncodeConfig; +} + +void NvEncoder::InitializeBitstreamBuffer() { + for (int i = 0; i < m_nEncoderBuffer; i++) { + NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBuffer = { + NV_ENC_CREATE_BITSTREAM_BUFFER_VER}; + NVENC_API_CALL( + m_nvenc.nvEncCreateBitstreamBuffer(m_hEncoder, &createBitstreamBuffer)); + m_vBitstreamOutputBuffer[i] = createBitstreamBuffer.bitstreamBuffer; + } +} + +void NvEncoder::DestroyBitstreamBuffer() { + for (uint32_t i = 0; i < m_vBitstreamOutputBuffer.size(); i++) { + if (m_vBitstreamOutputBuffer[i]) { + m_nvenc.nvEncDestroyBitstreamBuffer(m_hEncoder, + m_vBitstreamOutputBuffer[i]); + } + } + + m_vBitstreamOutputBuffer.clear(); +} + +void NvEncoder::InitializeMVOutputBuffer() { + for (int i = 0; i < m_nEncoderBuffer; i++) { + NV_ENC_CREATE_MV_BUFFER createMVBuffer = {NV_ENC_CREATE_MV_BUFFER_VER}; + NVENC_API_CALL(m_nvenc.nvEncCreateMVBuffer(m_hEncoder, &createMVBuffer)); + m_vMVDataOutputBuffer.push_back(createMVBuffer.mvBuffer); + } +} + +void NvEncoder::DestroyMVOutputBuffer() { + for (uint32_t i = 0; i < m_vMVDataOutputBuffer.size(); i++) { + if (m_vMVDataOutputBuffer[i]) { + m_nvenc.nvEncDestroyMVBuffer(m_hEncoder, m_vMVDataOutputBuffer[i]); + } + } + + m_vMVDataOutputBuffer.clear(); +} + +NVENCSTATUS NvEncoder::DoMotionEstimation( + NV_ENC_INPUT_PTR inputBuffer, NV_ENC_INPUT_PTR inputBufferForReference, + NV_ENC_OUTPUT_PTR outputBuffer) { + NV_ENC_MEONLY_PARAMS meParams = {NV_ENC_MEONLY_PARAMS_VER}; + meParams.inputBuffer = inputBuffer; + meParams.referenceFrame = inputBufferForReference; + meParams.inputWidth = GetEncodeWidth(); + meParams.inputHeight = GetEncodeHeight(); + meParams.mvBuffer = outputBuffer; + meParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer); + NVENCSTATUS nvStatus = + m_nvenc.nvEncRunMotionEstimationOnly(m_hEncoder, &meParams); + + return nvStatus; +} diff --git a/src/media/video/encode/nvcodec/NvEncoder.h b/src/media/video/encode/nvcodec/NvEncoder.h new file mode 100644 index 0000000..2c4419f --- /dev/null +++ b/src/media/video/encode/nvcodec/NvEncoder.h @@ -0,0 +1,482 @@ +/* + * Copyright 2017-2020 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +#include "nvEncodeAPI.h" + +/** + * @brief Exception class for error reporting from NvEncodeAPI calls. + */ +class NVENCException : public std::exception { + public: + NVENCException(const std::string& errorStr, const NVENCSTATUS errorCode) + : m_errorString(errorStr), m_errorCode(errorCode) {} + + virtual ~NVENCException() throw() {} + virtual const char* what() const throw() { return m_errorString.c_str(); } + NVENCSTATUS getErrorCode() const { return m_errorCode; } + const std::string& getErrorString() const { return m_errorString; } + static NVENCException makeNVENCException(const std::string& errorStr, + const NVENCSTATUS errorCode, + const std::string& functionName, + const std::string& fileName, + int lineNo); + + private: + std::string m_errorString; + NVENCSTATUS m_errorCode; +}; + +inline NVENCException NVENCException::makeNVENCException( + const std::string& errorStr, const NVENCSTATUS errorCode, + const std::string& functionName, const std::string& fileName, int lineNo) { + std::ostringstream errorLog; + errorLog << functionName << " : " << errorStr << " at " << fileName << ":" + << lineNo << std::endl; + NVENCException exception(errorLog.str(), errorCode); + return exception; +} + +#define NVENC_THROW_ERROR(errorStr, errorCode) \ + do { \ + throw NVENCException::makeNVENCException( \ + errorStr, errorCode, __FUNCTION__, __FILE__, __LINE__); \ + } while (0) + +#define NVENC_API_CALL(nvencAPI) \ + do { \ + NVENCSTATUS errorCode = nvencAPI; \ + if (errorCode != NV_ENC_SUCCESS) { \ + std::ostringstream errorLog; \ + errorLog << #nvencAPI << " returned error " << errorCode; \ + throw NVENCException::makeNVENCException( \ + errorLog.str(), errorCode, __FUNCTION__, __FILE__, __LINE__); \ + } \ + } while (0) + +struct NvEncInputFrame { + void* inputPtr = nullptr; + uint32_t chromaOffsets[2]; + uint32_t numChromaPlanes; + uint32_t pitch; + uint32_t chromaPitch; + NV_ENC_BUFFER_FORMAT bufferFormat; + NV_ENC_INPUT_RESOURCE_TYPE resourceType; +}; + +/** + * @brief Shared base class for different encoder interfaces. + */ +class NvEncoder { + public: + /** + * @brief This function is used to initialize the encoder session. + * Application must call this function to initialize the encoder, before + * starting to encode any frames. + */ + void CreateEncoder(const NV_ENC_INITIALIZE_PARAMS* pEncodeParams); + + /** + * @brief This function is used to destroy the encoder session. + * Application must call this function to destroy the encoder session and + * clean up any allocated resources. The application must call EndEncode() + * function to get any queued encoded frames before calling DestroyEncoder(). + */ + void DestroyEncoder(); + + /** + * @brief This function is used to reconfigure an existing encoder session. + * Application can use this function to dynamically change the bitrate, + * resolution and other QOS parameters. If the application changes the + * resolution, it must set NV_ENC_RECONFIGURE_PARAMS::forceIDR. + */ + bool Reconfigure(const NV_ENC_RECONFIGURE_PARAMS* pReconfigureParams); + + /** + * @brief This function is used to get the next available input buffer. + * Applications must call this function to obtain a pointer to the next + * input buffer. The application must copy the uncompressed data to the + * input buffer and then call EncodeFrame() function to encode it. + */ + const NvEncInputFrame* GetNextInputFrame(); + + /** + * @brief This function is used to encode a frame. + * Applications must call EncodeFrame() function to encode the uncompressed + * data, which has been copied to an input buffer obtained from the + * GetNextInputFrame() function. + */ + void EncodeFrame(std::vector>& vPacket, + NV_ENC_PIC_PARAMS* pPicParams = nullptr); + + /** + * @brief This function to flush the encoder queue. + * The encoder might be queuing frames for B picture encoding or lookahead; + * the application must call EndEncode() to get all the queued encoded frames + * from the encoder. The application must call this function before + * destroying an encoder session. + */ + void EndEncode(std::vector>& vPacket); + + /** + * @brief This function is used to query hardware encoder capabilities. + * Applications can call this function to query capabilities like maximum + * encode dimensions, support for lookahead or the ME-only mode etc. + */ + int GetCapabilityValue(GUID guidCodec, NV_ENC_CAPS capsToQuery); + + /** + * @brief This function is used to get the current device on which encoder + * is running. + */ + void* GetDevice() const { return m_pDevice; } + + /** + * @brief This function is used to get the current device type which encoder + * is running. + */ + NV_ENC_DEVICE_TYPE GetDeviceType() const { return m_eDeviceType; } + + /** + * @brief This function is used to get the current encode width. + * The encode width can be modified by Reconfigure() function. + */ + int GetEncodeWidth() const { return m_nWidth; } + + /** + * @brief This function is used to get the current encode height. + * The encode height can be modified by Reconfigure() function. + */ + int GetEncodeHeight() const { return m_nHeight; } + + /** + * @brief This function is used to get the current frame size based on + * pixel format. + */ + int GetFrameSize() const; + + /** + * @brief This function is used to initialize config parameters based on + * given codec and preset guids. + * The application can call this function to get the default configuration + * for a certain preset. The application can either use these parameters + * directly or override them with application-specific settings before + * using them in CreateEncoder() function. + */ + void CreateDefaultEncoderParams( + NV_ENC_INITIALIZE_PARAMS* pIntializeParams, GUID codecGuid, + GUID presetGuid, + NV_ENC_TUNING_INFO tuningInfo = NV_ENC_TUNING_INFO_UNDEFINED); + + /** + * @brief This function is used to get the current initialization + * parameters, which had been used to configure the encoder session. The + * initialization parameters are modified if the application calls + * Reconfigure() function. + */ + void GetInitializeParams(NV_ENC_INITIALIZE_PARAMS* pInitializeParams); + + /** + * @brief This function is used to run motion estimation + * This is used to run motion estimation on a a pair of frames. The + * application must copy the reference frame data to the buffer obtained + * by calling GetNextReferenceFrame(), and copy the input frame data to + * the buffer obtained by calling GetNextInputFrame() before calling the + * RunMotionEstimation() function. + */ + void RunMotionEstimation(std::vector& mvData); + + /** + * @brief This function is used to get an available reference frame. + * Application must call this function to get a pointer to reference buffer, + * to be used in the subsequent RunMotionEstimation() function. + */ + const NvEncInputFrame* GetNextReferenceFrame(); + + /** + * @brief This function is used to get sequence and picture parameter + * headers. Application can call this function after encoder is initialized to + * get SPS and PPS nalus for the current encoder instance. The sequence header + * data might change when application calls Reconfigure() function. + */ + void GetSequenceParams(std::vector& seqParams); + + /** + * @brief NvEncoder class virtual destructor. + */ + virtual ~NvEncoder(); + + public: + /** + * @brief This a static function to get chroma offsets for YUV planar + * formats. + */ + static void GetChromaSubPlaneOffsets(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t pitch, + const uint32_t height, + std::vector& chromaOffsets); + /** + * @brief This a static function to get the chroma plane pitch for YUV planar + * formats. + */ + static uint32_t GetChromaPitch(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t lumaPitch); + + /** + * @brief This a static function to get the number of chroma planes for YUV + * planar formats. + */ + static uint32_t GetNumChromaPlanes(const NV_ENC_BUFFER_FORMAT bufferFormat); + + /** + * @brief This a static function to get the chroma plane width in bytes for + * YUV planar formats. + */ + static uint32_t GetChromaWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t lumaWidth); + + /** + * @brief This a static function to get the chroma planes height in bytes for + * YUV planar formats. + */ + static uint32_t GetChromaHeight(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t lumaHeight); + + /** + * @brief This a static function to get the width in bytes for the frame. + * For YUV planar format this is the width in bytes of the luma plane. + */ + static uint32_t GetWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t width); + + /** + * @brief This function returns the number of allocated buffers. + */ + uint32_t GetEncoderBufferCount() const { return m_nEncoderBuffer; } + + protected: + /** + * @brief NvEncoder class constructor. + * NvEncoder class constructor cannot be called directly by the application. + */ + NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, void* pDevice, uint32_t nWidth, + uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat, + uint32_t nOutputDelay, bool bMotionEstimationOnly, + bool bOutputInVideoMemory = false); + + /** + * @brief This function is used to check if hardware encoder is properly + * initialized. + */ + bool IsHWEncoderInitialized() const { + return m_hEncoder != NULL && m_bEncoderInitialized; + } + + /** + * @brief This function is used to register CUDA, D3D or OpenGL input buffers + * with NvEncodeAPI. This is non public function and is called by derived + * class for allocating and registering input buffers. + */ + void RegisterInputResources(std::vector inputframes, + NV_ENC_INPUT_RESOURCE_TYPE eResourceType, + int width, int height, int pitch, + NV_ENC_BUFFER_FORMAT bufferFormat, + bool bReferenceFrame = false); + + /** + * @brief This function is used to unregister resources which had been + * previously registered for encoding using RegisterInputResources() function. + */ + void UnregisterInputResources(); + + /** + * @brief This function is used to register CUDA, D3D or OpenGL input or + * output buffers with NvEncodeAPI. + */ + NV_ENC_REGISTERED_PTR RegisterResource( + void* pBuffer, NV_ENC_INPUT_RESOURCE_TYPE eResourceType, int width, + int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, + NV_ENC_BUFFER_USAGE bufferUsage = NV_ENC_INPUT_IMAGE); + + /** + * @brief This function returns maximum width used to open the encoder + * session. All encode input buffers are allocated using maximum dimensions. + */ + uint32_t GetMaxEncodeWidth() const { return m_nMaxEncodeWidth; } + + /** + * @brief This function returns maximum height used to open the encoder + * session. All encode input buffers are allocated using maximum dimensions. + */ + uint32_t GetMaxEncodeHeight() const { return m_nMaxEncodeHeight; } + + /** + * @brief This function returns the completion event. + */ + void* GetCompletionEvent(uint32_t eventIdx) { + return (m_vpCompletionEvent.size() == m_nEncoderBuffer) + ? m_vpCompletionEvent[eventIdx] + : nullptr; + } + + /** + * @brief This function returns the current pixel format. + */ + NV_ENC_BUFFER_FORMAT GetPixelFormat() const { return m_eBufferFormat; } + + /** + * @brief This function is used to submit the encode commands to the + * NVENC hardware. + */ + NVENCSTATUS DoEncode(NV_ENC_INPUT_PTR inputBuffer, + NV_ENC_OUTPUT_PTR outputBuffer, + NV_ENC_PIC_PARAMS* pPicParams); + + /** + * @brief This function is used to submit the encode commands to the + * NVENC hardware for ME only mode. + */ + NVENCSTATUS DoMotionEstimation(NV_ENC_INPUT_PTR inputBuffer, + NV_ENC_INPUT_PTR inputBufferForReference, + NV_ENC_OUTPUT_PTR outputBuffer); + + /** + * @brief This function is used to map the input buffers to NvEncodeAPI. + */ + void MapResources(uint32_t bfrIdx); + + /** + * @brief This function is used to wait for completion of encode command. + */ + void WaitForCompletionEvent(int iEvent); + + /** + * @brief This function is used to send EOS to HW encoder. + */ + void SendEOS(); + + private: + /** + * @brief This is a private function which is used to check if there is any + buffering done by encoder. + * The encoder generally buffers data to encode B frames or for lookahead + * or pipelining. + */ + bool IsZeroDelay() { return m_nOutputDelay == 0; } + + /** + * @brief This is a private function which is used to load the encode api + * shared library. + */ + void LoadNvEncApi(); + + /** + * @brief This is a private function which is used to get the output packets + * from the encoder HW. + * This is called by DoEncode() function. If there is buffering enabled, + * this may return without any output data. + */ + void GetEncodedPacket(std::vector& vOutputBuffer, + std::vector>& vPacket, + bool bOutputDelay); + + /** + * @brief This is a private function which is used to initialize the + * bitstream buffers. This is only used in the encoding mode. + */ + void InitializeBitstreamBuffer(); + + /** + * @brief This is a private function which is used to destroy the bitstream + * buffers. This is only used in the encoding mode. + */ + void DestroyBitstreamBuffer(); + + /** + * @brief This is a private function which is used to initialize MV output + * buffers. This is only used in ME-only Mode. + */ + void InitializeMVOutputBuffer(); + + /** + * @brief This is a private function which is used to destroy MV output + * buffers. This is only used in ME-only Mode. + */ + void DestroyMVOutputBuffer(); + + /** + * @brief This is a private function which is used to destroy HW encoder. + */ + void DestroyHWEncoder(); + + /** + * @brief This function is used to flush the encoder queue. + */ + void FlushEncoder(); + + private: + /** + * @brief This is a pure virtual function which is used to allocate input + * buffers. The derived classes must implement this function. + */ + virtual void AllocateInputBuffers(int32_t numInputBuffers) = 0; + + /** + * @brief This is a pure virtual function which is used to destroy input + * buffers. The derived classes must implement this function. + */ + virtual void ReleaseInputBuffers() = 0; + + protected: + bool m_bMotionEstimationOnly = false; + bool m_bOutputInVideoMemory = false; + void* m_hEncoder = nullptr; + NV_ENCODE_API_FUNCTION_LIST m_nvenc; + std::vector m_vInputFrames; + std::vector m_vRegisteredResources; + std::vector m_vReferenceFrames; + std::vector m_vRegisteredResourcesForReference; + std::vector m_vMappedInputBuffers; + std::vector m_vMappedRefBuffers; + std::vector m_vpCompletionEvent; + + int32_t m_iToSend = 0; + int32_t m_iGot = 0; + int32_t m_nEncoderBuffer = 0; + int32_t m_nOutputDelay = 0; + + private: + uint32_t m_nWidth; + uint32_t m_nHeight; + NV_ENC_BUFFER_FORMAT m_eBufferFormat; + void* m_pDevice; + NV_ENC_DEVICE_TYPE m_eDeviceType; + NV_ENC_INITIALIZE_PARAMS m_initializeParams = {}; + NV_ENC_CONFIG m_encodeConfig = {}; + bool m_bEncoderInitialized = false; + uint32_t m_nExtraOutputDelay = + 3; // To ensure encode and graphics can work in parallel, + // m_nExtraOutputDelay should be set to at least 1 + std::vector m_vBitstreamOutputBuffer; + std::vector m_vMVDataOutputBuffer; + uint32_t m_nMaxEncodeWidth = 0; + uint32_t m_nMaxEncodeHeight = 0; +}; diff --git a/src/media/video/encode/nvcodec/NvEncoderCuda.cpp b/src/media/video/encode/nvcodec/NvEncoderCuda.cpp new file mode 100644 index 0000000..91cf62e --- /dev/null +++ b/src/media/video/encode/nvcodec/NvEncoderCuda.cpp @@ -0,0 +1,244 @@ +/* + * Copyright 2017-2020 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +#include "NvEncoderCuda.h" + +NvEncoderCuda::NvEncoderCuda(CUcontext cuContext, uint32_t nWidth, + uint32_t nHeight, + NV_ENC_BUFFER_FORMAT eBufferFormat, + uint32_t nExtraOutputDelay, + bool bMotionEstimationOnly, + bool bOutputInVideoMemory) + : NvEncoder(NV_ENC_DEVICE_TYPE_CUDA, cuContext, nWidth, nHeight, + eBufferFormat, nExtraOutputDelay, bMotionEstimationOnly, + bOutputInVideoMemory), + m_cuContext(cuContext) { + if (!m_hEncoder) { + NVENC_THROW_ERROR("Encoder Initialization failed", + NV_ENC_ERR_INVALID_DEVICE); + } + + if (!m_cuContext) { + NVENC_THROW_ERROR("Invalid Cuda Context", NV_ENC_ERR_INVALID_DEVICE); + } +} + +NvEncoderCuda::~NvEncoderCuda() { ReleaseCudaResources(); } + +void NvEncoderCuda::AllocateInputBuffers(int32_t numInputBuffers) { + if (!IsHWEncoderInitialized()) { + NVENC_THROW_ERROR("Encoder intialization failed", + NV_ENC_ERR_ENCODER_NOT_INITIALIZED); + } + + // for MEOnly mode we need to allocate seperate set of buffers for reference + // frame + int numCount = m_bMotionEstimationOnly ? 2 : 1; + + for (int count = 0; count < numCount; count++) { + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + std::vector inputFrames; + for (int i = 0; i < numInputBuffers; i++) { + CUdeviceptr pDeviceFrame; + uint32_t chromaHeight = + GetNumChromaPlanes(GetPixelFormat()) * + GetChromaHeight(GetPixelFormat(), GetMaxEncodeHeight()); + if (GetPixelFormat() == NV_ENC_BUFFER_FORMAT_YV12 || + GetPixelFormat() == NV_ENC_BUFFER_FORMAT_IYUV) + chromaHeight = GetChromaHeight(GetPixelFormat(), GetMaxEncodeHeight()); + CUDA_DRVAPI_CALL(cuMemAllocPitch( + (CUdeviceptr *)&pDeviceFrame, &m_cudaPitch, + GetWidthInBytes(GetPixelFormat(), GetMaxEncodeWidth()), + GetMaxEncodeHeight() + chromaHeight, 16)); + inputFrames.push_back((void *)pDeviceFrame); + } + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + + RegisterInputResources( + inputFrames, NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR, + GetMaxEncodeWidth(), GetMaxEncodeHeight(), (int)m_cudaPitch, + GetPixelFormat(), (count == 1) ? true : false); + } +} + +void NvEncoderCuda::SetIOCudaStreams(NV_ENC_CUSTREAM_PTR inputStream, + NV_ENC_CUSTREAM_PTR outputStream) { + NVENC_API_CALL( + m_nvenc.nvEncSetIOCudaStreams(m_hEncoder, inputStream, outputStream)); +} + +void NvEncoderCuda::ReleaseInputBuffers() { ReleaseCudaResources(); } + +void NvEncoderCuda::ReleaseCudaResources() { + if (!m_hEncoder) { + return; + } + + if (!m_cuContext) { + return; + } + + UnregisterInputResources(); + + cuCtxPushCurrent(m_cuContext); + + for (uint32_t i = 0; i < m_vInputFrames.size(); ++i) { + if (m_vInputFrames[i].inputPtr) { + cuMemFree(reinterpret_cast(m_vInputFrames[i].inputPtr)); + } + } + m_vInputFrames.clear(); + + for (uint32_t i = 0; i < m_vReferenceFrames.size(); ++i) { + if (m_vReferenceFrames[i].inputPtr) { + cuMemFree(reinterpret_cast(m_vReferenceFrames[i].inputPtr)); + } + } + m_vReferenceFrames.clear(); + + cuCtxPopCurrent(NULL); + m_cuContext = nullptr; +} + +void NvEncoderCuda::CopyToDeviceFrame( + CUcontext device, void *pSrcFrame, uint32_t nSrcPitch, + CUdeviceptr pDstFrame, uint32_t dstPitch, int width, int height, + CUmemorytype srcMemoryType, NV_ENC_BUFFER_FORMAT pixelFormat, + const uint32_t dstChromaOffsets[], uint32_t numChromaPlanes, + bool bUnAlignedDeviceCopy, CUstream stream) { + if (srcMemoryType != CU_MEMORYTYPE_HOST && + srcMemoryType != CU_MEMORYTYPE_DEVICE) { + NVENC_THROW_ERROR("Invalid source memory type for copy", + NV_ENC_ERR_INVALID_PARAM); + } + + CUDA_DRVAPI_CALL(cuCtxPushCurrent(device)); + + uint32_t srcPitch = + nSrcPitch ? nSrcPitch : NvEncoder::GetWidthInBytes(pixelFormat, width); + CUDA_MEMCPY2D m = {0}; + m.srcMemoryType = srcMemoryType; + if (srcMemoryType == CU_MEMORYTYPE_HOST) { + m.srcHost = pSrcFrame; + } else { + m.srcDevice = (CUdeviceptr)pSrcFrame; + } + m.srcPitch = srcPitch; + m.dstMemoryType = CU_MEMORYTYPE_DEVICE; + m.dstDevice = pDstFrame; + m.dstPitch = dstPitch; + m.WidthInBytes = NvEncoder::GetWidthInBytes(pixelFormat, width); + m.Height = height; + if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) { + CUDA_DRVAPI_CALL(cuMemcpy2DUnaligned(&m)); + } else { + CUDA_DRVAPI_CALL(stream == NULL ? cuMemcpy2D(&m) + : cuMemcpy2DAsync(&m, stream)); + } + + std::vector srcChromaOffsets; + NvEncoder::GetChromaSubPlaneOffsets(pixelFormat, srcPitch, height, + srcChromaOffsets); + uint32_t chromaHeight = NvEncoder::GetChromaHeight(pixelFormat, height); + uint32_t destChromaPitch = NvEncoder::GetChromaPitch(pixelFormat, dstPitch); + uint32_t srcChromaPitch = NvEncoder::GetChromaPitch(pixelFormat, srcPitch); + uint32_t chromaWidthInBytes = + NvEncoder::GetChromaWidthInBytes(pixelFormat, width); + + for (uint32_t i = 0; i < numChromaPlanes; ++i) { + if (chromaHeight) { + if (srcMemoryType == CU_MEMORYTYPE_HOST) { + m.srcHost = ((uint8_t *)pSrcFrame + srcChromaOffsets[i]); + } else { + m.srcDevice = (CUdeviceptr)((uint8_t *)pSrcFrame + srcChromaOffsets[i]); + } + m.srcPitch = srcChromaPitch; + + m.dstDevice = (CUdeviceptr)((uint8_t *)pDstFrame + dstChromaOffsets[i]); + m.dstPitch = destChromaPitch; + m.WidthInBytes = chromaWidthInBytes; + m.Height = chromaHeight; + if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) { + CUDA_DRVAPI_CALL(cuMemcpy2DUnaligned(&m)); + } else { + CUDA_DRVAPI_CALL(stream == NULL ? cuMemcpy2D(&m) + : cuMemcpy2DAsync(&m, stream)); + } + } + } + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); +} + +void NvEncoderCuda::CopyToDeviceFrame( + CUcontext device, void *pSrcFrame, uint32_t nSrcPitch, + CUdeviceptr pDstFrame, uint32_t dstPitch, int width, int height, + CUmemorytype srcMemoryType, NV_ENC_BUFFER_FORMAT pixelFormat, + CUdeviceptr dstChromaDevicePtrs[], uint32_t dstChromaPitch, + uint32_t numChromaPlanes, bool bUnAlignedDeviceCopy) { + if (srcMemoryType != CU_MEMORYTYPE_HOST && + srcMemoryType != CU_MEMORYTYPE_DEVICE) { + NVENC_THROW_ERROR("Invalid source memory type for copy", + NV_ENC_ERR_INVALID_PARAM); + } + + CUDA_DRVAPI_CALL(cuCtxPushCurrent(device)); + + uint32_t srcPitch = + nSrcPitch ? nSrcPitch : NvEncoder::GetWidthInBytes(pixelFormat, width); + CUDA_MEMCPY2D m = {0}; + m.srcMemoryType = srcMemoryType; + if (srcMemoryType == CU_MEMORYTYPE_HOST) { + m.srcHost = pSrcFrame; + } else { + m.srcDevice = (CUdeviceptr)pSrcFrame; + } + m.srcPitch = srcPitch; + m.dstMemoryType = CU_MEMORYTYPE_DEVICE; + m.dstDevice = pDstFrame; + m.dstPitch = dstPitch; + m.WidthInBytes = NvEncoder::GetWidthInBytes(pixelFormat, width); + m.Height = height; + if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) { + CUDA_DRVAPI_CALL(cuMemcpy2DUnaligned(&m)); + } else { + CUDA_DRVAPI_CALL(cuMemcpy2D(&m)); + } + + std::vector srcChromaOffsets; + NvEncoder::GetChromaSubPlaneOffsets(pixelFormat, srcPitch, height, + srcChromaOffsets); + uint32_t chromaHeight = NvEncoder::GetChromaHeight(pixelFormat, height); + uint32_t srcChromaPitch = NvEncoder::GetChromaPitch(pixelFormat, srcPitch); + uint32_t chromaWidthInBytes = + NvEncoder::GetChromaWidthInBytes(pixelFormat, width); + + for (uint32_t i = 0; i < numChromaPlanes; ++i) { + if (chromaHeight) { + if (srcMemoryType == CU_MEMORYTYPE_HOST) { + m.srcHost = ((uint8_t *)pSrcFrame + srcChromaOffsets[i]); + } else { + m.srcDevice = (CUdeviceptr)((uint8_t *)pSrcFrame + srcChromaOffsets[i]); + } + m.srcPitch = srcChromaPitch; + + m.dstDevice = dstChromaDevicePtrs[i]; + m.dstPitch = dstChromaPitch; + m.WidthInBytes = chromaWidthInBytes; + m.Height = chromaHeight; + if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) { + CUDA_DRVAPI_CALL(cuMemcpy2DUnaligned(&m)); + } else { + CUDA_DRVAPI_CALL(cuMemcpy2D(&m)); + } + } + } + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); +} diff --git a/src/media/video/encode/nvcodec/NvEncoderCuda.h b/src/media/video/encode/nvcodec/NvEncoderCuda.h new file mode 100644 index 0000000..ad9e80f --- /dev/null +++ b/src/media/video/encode/nvcodec/NvEncoderCuda.h @@ -0,0 +1,106 @@ +/* + * Copyright 2017-2020 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +#pragma once + +#include +#include + +#include +#include + +#include "NvEncoder.h" + +#define CUDA_DRVAPI_CALL(call) \ + do { \ + CUresult err__ = call; \ + if (err__ != CUDA_SUCCESS) { \ + const char* szErrName = NULL; \ + cuGetErrorName(err__, &szErrName); \ + std::ostringstream errorLog; \ + errorLog << "CUDA driver API error " << szErrName; \ + throw NVENCException::makeNVENCException( \ + errorLog.str(), NV_ENC_ERR_GENERIC, __FUNCTION__, __FILE__, \ + __LINE__); \ + } \ + } while (0) + +/** + * @brief Encoder for CUDA device memory. + */ +class NvEncoderCuda : public NvEncoder { + public: + NvEncoderCuda(CUcontext cuContext, uint32_t nWidth, uint32_t nHeight, + NV_ENC_BUFFER_FORMAT eBufferFormat, + uint32_t nExtraOutputDelay = 3, + bool bMotionEstimationOnly = false, + bool bOPInVideoMemory = false); + virtual ~NvEncoderCuda(); + + /** + * @brief This is a static function to copy input data from host memory to + * device memory. This function assumes YUV plane is a single contiguous + * memory segment. + */ + static void CopyToDeviceFrame( + CUcontext device, void* pSrcFrame, uint32_t nSrcPitch, + CUdeviceptr pDstFrame, uint32_t dstPitch, int width, int height, + CUmemorytype srcMemoryType, NV_ENC_BUFFER_FORMAT pixelFormat, + const uint32_t dstChromaOffsets[], uint32_t numChromaPlanes, + bool bUnAlignedDeviceCopy = false, CUstream stream = NULL); + + /** + * @brief This is a static function to copy input data from host memory to + * device memory. Application must pass a seperate device pointer for each YUV + * plane. + */ + static void CopyToDeviceFrame( + CUcontext device, void* pSrcFrame, uint32_t nSrcPitch, + CUdeviceptr pDstFrame, uint32_t dstPitch, int width, int height, + CUmemorytype srcMemoryType, NV_ENC_BUFFER_FORMAT pixelFormat, + CUdeviceptr dstChromaPtr[], uint32_t dstChromaPitch, + uint32_t numChromaPlanes, bool bUnAlignedDeviceCopy = false); + + /** + * @brief This function sets input and output CUDA streams + */ + void SetIOCudaStreams(NV_ENC_CUSTREAM_PTR inputStream, + NV_ENC_CUSTREAM_PTR outputStream); + + protected: + /** + * @brief This function is used to release the input buffers allocated for + * encoding. This function is an override of virtual function + * NvEncoder::ReleaseInputBuffers(). + */ + virtual void ReleaseInputBuffers() override; + + private: + /** + * @brief This function is used to allocate input buffers for encoding. + * This function is an override of virtual function + * NvEncoder::AllocateInputBuffers(). + */ + virtual void AllocateInputBuffers(int32_t numInputBuffers) override; + + private: + /** + * @brief This is a private function to release CUDA device memory used for + * encoding. + */ + void ReleaseCudaResources(); + + protected: + CUcontext m_cuContext; + + private: + size_t m_cudaPitch = 0; +}; diff --git a/src/media/video/encode/nvcodec/nv_encoder.cpp b/src/media/video/encode/nvcodec/nv_encoder.cpp new file mode 100644 index 0000000..592791b --- /dev/null +++ b/src/media/video/encode/nvcodec/nv_encoder.cpp @@ -0,0 +1,145 @@ +#include "nv_encoder.h" + +#include + +#include "log.h" + +#define SAVE_ENCODER_STREAM 0 + +VideoEncoder::VideoEncoder() { + if (SAVE_ENCODER_STREAM) { + file_ = fopen("saved/stream.h264", "w+b"); + if (!file_) { + LOG_WARN("Fail to open saved/stream.h264"); + } + } +} +VideoEncoder::~VideoEncoder() { + if (SAVE_ENCODER_STREAM && file_) { + fflush(file_); + fclose(file_); + file_ = nullptr; + } + + if (nv12_data_) { + free(nv12_data_); + nv12_data_ = nullptr; + } +} + +int VideoEncoder::Init() { + // Init cuda context + int num_of_GPUs = 0; + CUdevice cuda_device; + bool cuda_ctx_succeed = + (index_of_GPU >= 0 && cuInit(0) == CUresult::CUDA_SUCCESS && + cuDeviceGetCount(&num_of_GPUs) == CUresult::CUDA_SUCCESS && + (num_of_GPUs > 0 && index_of_GPU < num_of_GPUs) && + cuDeviceGet(&cuda_device, index_of_GPU) == CUresult::CUDA_SUCCESS && + cuCtxCreate(&cuda_context_, 0, cuda_device) == CUresult::CUDA_SUCCESS); + if (!cuda_ctx_succeed) { + } + + encoder_ = new NvEncoderCuda(cuda_context_, frame_width, frame_height, + NV_ENC_BUFFER_FORMAT::NV_ENC_BUFFER_FORMAT_NV12); + + // Init encoder_ session + NV_ENC_INITIALIZE_PARAMS init_params; + init_params.version = NV_ENC_INITIALIZE_PARAMS_VER; + NV_ENC_CONFIG encode_config = {NV_ENC_CONFIG_VER}; + init_params.encodeConfig = &encode_config; + + encoder_->CreateDefaultEncoderParams(&init_params, codec_guid, preset_guid, + tuning_info); + + init_params.encodeWidth = frame_width; + init_params.encodeHeight = frame_height; + init_params.encodeConfig->profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID; + init_params.encodeConfig->encodeCodecConfig.h264Config.level = + NV_ENC_LEVEL::NV_ENC_LEVEL_H264_31; + // TO TEST: not tested yet + // init_params.encodeConfig->gopLength = NVENC_INFINITE_GOPLENGTH; + init_params.encodeConfig->gopLength = keyFrameInterval_; + // Donot use B-frame for realtime application + init_params.encodeConfig->frameIntervalP = 1; + init_params.encodeConfig->rcParams.rateControlMode = + NV_ENC_PARAMS_RC_MODE::NV_ENC_PARAMS_RC_CBR; + init_params.encodeConfig->rcParams.maxBitRate = maxBitrate_ * 1000; + init_params.encodeConfig->encodeCodecConfig.h264Config.sliceMode = 1; + init_params.encodeConfig->encodeCodecConfig.h264Config.sliceModeData = + max_payload_size_; + + encoder_->CreateEncoder(&init_params); + return 0; +} + +int VideoEncoder::Encode(const uint8_t *pData, int nSize) { + if (!encoder_) { + LOG_ERROR("Invalid encoder"); + return -1; + } + + if (0 == seq_++ % (30 * 5)) { + ForceIdr(); + } + +#ifdef SHOW_SUBMODULE_TIME_COST + auto start = std::chrono::steady_clock::now(); +#endif + + const NvEncInputFrame *encoder_inputframe = encoder_->GetNextInputFrame(); + + NvEncoderCuda::CopyToDeviceFrame( + cuda_context_, + (void *)pData, // NOLINT + 0, (CUdeviceptr)encoder_inputframe->inputPtr, encoder_inputframe->pitch, + encoder_->GetEncodeWidth(), encoder_->GetEncodeHeight(), + CU_MEMORYTYPE_HOST, encoder_inputframe->bufferFormat, + encoder_inputframe->chromaOffsets, encoder_inputframe->numChromaPlanes); + + encoder_->EncodeFrame(encoded_packets_); + + if (encoded_packets_.size() < 1) { + LOG_WARN("empty encoded_packets_"); + return -1; + } + + for (const auto &packet : encoded_packets_) { + OnEncodedImage((char *)packet.data(), packet.size()); + + if (SAVE_ENCODER_STREAM) { + fwrite((unsigned char *)packet.data(), 1, packet.size(), file_); + } + } + +#ifdef SHOW_SUBMODULE_TIME_COST + auto encode_time_cost = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start) + .count(); + LOG_INFO("Encode time cost {}ms", encode_time_cost); +#endif + + return 0; +} + +int VideoEncoder::OnEncodedImage(char *encoded_packets, size_t size) { + LOG_INFO("output encoded image"); + fwrite(encoded_packets, 1, size, file_); + return 0; +} + +void VideoEncoder::ForceIdr() { + NV_ENC_RECONFIGURE_PARAMS reconfig_params; + reconfig_params.version = NV_ENC_RECONFIGURE_PARAMS_VER; + + NV_ENC_INITIALIZE_PARAMS init_params; + NV_ENC_CONFIG encode_config = {NV_ENC_CONFIG_VER}; + init_params.encodeConfig = &encode_config; + encoder_->GetInitializeParams(&init_params); + + reconfig_params.reInitEncodeParams = init_params; + reconfig_params.forceIDR = 1; + reconfig_params.resetEncoder = 1; + + encoder_->Reconfigure(&reconfig_params); +} diff --git a/src/media/video/encode/nvcodec/nv_encoder.h b/src/media/video/encode/nvcodec/nv_encoder.h new file mode 100644 index 0000000..3767220 --- /dev/null +++ b/src/media/video/encode/nvcodec/nv_encoder.h @@ -0,0 +1,36 @@ +#ifndef _NV_ENCODER_H_ +#define _NV_ENCODER_H_ + +#include "NvEncoderCuda.h" + +class VideoEncoder { + public: + VideoEncoder(); + ~VideoEncoder(); + + int Init(); + int Encode(const uint8_t* pData, int nSize); + virtual int OnEncodedImage(char* encoded_packets, size_t size); + void ForceIdr(); + + private: + int index_of_GPU = 0; + GUID codec_guid = NV_ENC_CODEC_H264_GUID; + GUID preset_guid = NV_ENC_PRESET_P2_GUID; + NV_ENC_TUNING_INFO tuning_info = + NV_ENC_TUNING_INFO::NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY; + int frame_width = 1280; + int frame_height = 720; + int keyFrameInterval_ = 3000; + int maxBitrate_ = 2000; + int max_payload_size_ = 3000; + NvEncoder* encoder_ = nullptr; + CUcontext cuda_context_ = nullptr; + std::vector> encoded_packets_; + unsigned char* encoded_image_ = nullptr; + FILE* file_ = nullptr; + unsigned char* nv12_data_ = nullptr; + unsigned int seq_ = 0; +}; + +#endif \ No newline at end of file diff --git a/src/pc/peer_connection.cpp b/src/pc/peer_connection.cpp index b89fe68..34dfb59 100644 --- a/src/pc/peer_connection.cpp +++ b/src/pc/peer_connection.cpp @@ -50,6 +50,7 @@ int PeerConnection::Init(PeerConnectionParams params, do { } while (SignalStatus::Connected != GetSignalStatus()); + VideoEncoder::Init(); return 0; } @@ -215,7 +216,35 @@ int PeerConnection::Destroy() { SignalStatus PeerConnection::GetSignalStatus() { return signal_status_; } -int PeerConnection::SendData(const char *data, size_t size) { +int PeerConnection::SendVideoData(const char *data, size_t size) { + int ret = Encode((uint8_t *)data, size); + if (0 != ret) { + LOG_ERROR("Encode failed"); + return -1; + } + + // for (auto ice_trans : ice_transmission_list_) { + // ice_trans.second->SendData(data, size); + // } + return 0; +} + +int PeerConnection::OnEncodedImage(char *encoded_packets, size_t size) { + for (auto ice_trans : ice_transmission_list_) { + ice_trans.second->SendData(encoded_packets, size); + } + + return 0; +} + +int PeerConnection::SendAudioData(const char *data, size_t size) { + for (auto ice_trans : ice_transmission_list_) { + ice_trans.second->SendData(data, size); + } + return 0; +} + +int PeerConnection::SendUserData(const char *data, size_t size) { for (auto ice_trans : ice_transmission_list_) { ice_trans.second->SendData(data, size); } diff --git a/src/pc/peer_connection.h b/src/pc/peer_connection.h index 65f345c..d09603a 100644 --- a/src/pc/peer_connection.h +++ b/src/pc/peer_connection.h @@ -5,6 +5,8 @@ #include #include "ice_transmission.h" +#include "nv_decoder.h" +#include "nv_encoder.h" #include "ws_transmission.h" enum SignalStatus { Connecting = 0, Connected, Closed }; @@ -20,7 +22,7 @@ typedef struct { NetStatusReport net_status_report; } PeerConnectionParams; -class PeerConnection { +class PeerConnection : public VideoEncoder, VideoDecoder { public: PeerConnection(OnReceiveBuffer on_receive_buffer); ~PeerConnection(); @@ -37,7 +39,9 @@ class PeerConnection { SignalStatus GetSignalStatus(); - int SendData(const char *data, size_t size); + int SendVideoData(const char *data, size_t size); + int SendAudioData(const char *data, size_t size); + int SendUserData(const char *data, size_t size); private: int Init(PeerConnectionParams params, const std::string &transmission_id, @@ -47,6 +51,9 @@ class PeerConnection { int RequestTransmissionMemberList(const std::string &transmission_id); + private: + int OnEncodedImage(char *encoded_packets, size_t size) override; + private: std::string uri_ = ""; std::string cfg_signal_server_ip_; @@ -68,6 +75,8 @@ class PeerConnection { SignalStatus signal_status_ = SignalStatus::Closed; OnReceiveBuffer on_receive_buffer_; + + private: }; #endif \ No newline at end of file diff --git a/src/qos/kcp/ikcp.c b/src/qos/kcp/ikcp.c new file mode 100644 index 0000000..6e14bc9 --- /dev/null +++ b/src/qos/kcp/ikcp.c @@ -0,0 +1,1306 @@ +//===================================================================== +// +// KCP - A Better ARQ Protocol Implementation +// skywind3000 (at) gmail.com, 2010-2011 +// +// Features: +// + Average RTT reduce 30% - 40% vs traditional ARQ like tcp. +// + Maximum RTT reduce three times vs tcp. +// + Lightweight, distributed as a single source file. +// +//===================================================================== +#include "ikcp.h" + +#include +#include +#include +#include +#include + + + +//===================================================================== +// KCP BASIC +//===================================================================== +const IUINT32 IKCP_RTO_NDL = 30; // no delay min rto +const IUINT32 IKCP_RTO_MIN = 100; // normal min rto +const IUINT32 IKCP_RTO_DEF = 200; +const IUINT32 IKCP_RTO_MAX = 60000; +const IUINT32 IKCP_CMD_PUSH = 81; // cmd: push data +const IUINT32 IKCP_CMD_ACK = 82; // cmd: ack +const IUINT32 IKCP_CMD_WASK = 83; // cmd: window probe (ask) +const IUINT32 IKCP_CMD_WINS = 84; // cmd: window size (tell) +const IUINT32 IKCP_ASK_SEND = 1; // need to send IKCP_CMD_WASK +const IUINT32 IKCP_ASK_TELL = 2; // need to send IKCP_CMD_WINS +const IUINT32 IKCP_WND_SND = 32; +const IUINT32 IKCP_WND_RCV = 128; // must >= max fragment size +const IUINT32 IKCP_MTU_DEF = 1400; +const IUINT32 IKCP_ACK_FAST = 3; +const IUINT32 IKCP_INTERVAL = 100; +const IUINT32 IKCP_OVERHEAD = 24; +const IUINT32 IKCP_DEADLINK = 20; +const IUINT32 IKCP_THRESH_INIT = 2; +const IUINT32 IKCP_THRESH_MIN = 2; +const IUINT32 IKCP_PROBE_INIT = 7000; // 7 secs to probe window size +const IUINT32 IKCP_PROBE_LIMIT = 120000; // up to 120 secs to probe window +const IUINT32 IKCP_FASTACK_LIMIT = 5; // max times to trigger fastack + + +//--------------------------------------------------------------------- +// encode / decode +//--------------------------------------------------------------------- + +/* encode 8 bits unsigned int */ +static inline char *ikcp_encode8u(char *p, unsigned char c) +{ + *(unsigned char*)p++ = c; + return p; +} + +/* decode 8 bits unsigned int */ +static inline const char *ikcp_decode8u(const char *p, unsigned char *c) +{ + *c = *(unsigned char*)p++; + return p; +} + +/* encode 16 bits unsigned int (lsb) */ +static inline char *ikcp_encode16u(char *p, unsigned short w) +{ +#if IWORDS_BIG_ENDIAN || IWORDS_MUST_ALIGN + *(unsigned char*)(p + 0) = (w & 255); + *(unsigned char*)(p + 1) = (w >> 8); +#else + memcpy(p, &w, 2); +#endif + p += 2; + return p; +} + +/* decode 16 bits unsigned int (lsb) */ +static inline const char *ikcp_decode16u(const char *p, unsigned short *w) +{ +#if IWORDS_BIG_ENDIAN || IWORDS_MUST_ALIGN + *w = *(const unsigned char*)(p + 1); + *w = *(const unsigned char*)(p + 0) + (*w << 8); +#else + memcpy(w, p, 2); +#endif + p += 2; + return p; +} + +/* encode 32 bits unsigned int (lsb) */ +static inline char *ikcp_encode32u(char *p, IUINT32 l) +{ +#if IWORDS_BIG_ENDIAN || IWORDS_MUST_ALIGN + *(unsigned char*)(p + 0) = (unsigned char)((l >> 0) & 0xff); + *(unsigned char*)(p + 1) = (unsigned char)((l >> 8) & 0xff); + *(unsigned char*)(p + 2) = (unsigned char)((l >> 16) & 0xff); + *(unsigned char*)(p + 3) = (unsigned char)((l >> 24) & 0xff); +#else + memcpy(p, &l, 4); +#endif + p += 4; + return p; +} + +/* decode 32 bits unsigned int (lsb) */ +static inline const char *ikcp_decode32u(const char *p, IUINT32 *l) +{ +#if IWORDS_BIG_ENDIAN || IWORDS_MUST_ALIGN + *l = *(const unsigned char*)(p + 3); + *l = *(const unsigned char*)(p + 2) + (*l << 8); + *l = *(const unsigned char*)(p + 1) + (*l << 8); + *l = *(const unsigned char*)(p + 0) + (*l << 8); +#else + memcpy(l, p, 4); +#endif + p += 4; + return p; +} + +static inline IUINT32 _imin_(IUINT32 a, IUINT32 b) { + return a <= b ? a : b; +} + +static inline IUINT32 _imax_(IUINT32 a, IUINT32 b) { + return a >= b ? a : b; +} + +static inline IUINT32 _ibound_(IUINT32 lower, IUINT32 middle, IUINT32 upper) +{ + return _imin_(_imax_(lower, middle), upper); +} + +static inline long _itimediff(IUINT32 later, IUINT32 earlier) +{ + return ((IINT32)(later - earlier)); +} + +//--------------------------------------------------------------------- +// manage segment +//--------------------------------------------------------------------- +typedef struct IKCPSEG IKCPSEG; + +static void* (*ikcp_malloc_hook)(size_t) = NULL; +static void (*ikcp_free_hook)(void *) = NULL; + +// internal malloc +static void* ikcp_malloc(size_t size) { + if (ikcp_malloc_hook) + return ikcp_malloc_hook(size); + return malloc(size); +} + +// internal free +static void ikcp_free(void *ptr) { + if (ikcp_free_hook) { + ikcp_free_hook(ptr); + } else { + free(ptr); + } +} + +// redefine allocator +void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*)) +{ + ikcp_malloc_hook = new_malloc; + ikcp_free_hook = new_free; +} + +// allocate a new kcp segment +static IKCPSEG* ikcp_segment_new(ikcpcb *kcp, int size) +{ + return (IKCPSEG*)ikcp_malloc(sizeof(IKCPSEG) + size); +} + +// delete a segment +static void ikcp_segment_delete(ikcpcb *kcp, IKCPSEG *seg) +{ + ikcp_free(seg); +} + +// write log +void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...) +{ + char buffer[1024]; + va_list argptr; + if ((mask & kcp->logmask) == 0 || kcp->writelog == 0) return; + va_start(argptr, fmt); + vsprintf(buffer, fmt, argptr); + va_end(argptr); + kcp->writelog(buffer, kcp, kcp->user); +} + +// check log mask +static int ikcp_canlog(const ikcpcb *kcp, int mask) +{ + if ((mask & kcp->logmask) == 0 || kcp->writelog == NULL) return 0; + return 1; +} + +// output segment +static int ikcp_output(ikcpcb *kcp, const void *data, int size) +{ + assert(kcp); + assert(kcp->output); + if (ikcp_canlog(kcp, IKCP_LOG_OUTPUT)) { + ikcp_log(kcp, IKCP_LOG_OUTPUT, "[RO] %ld bytes", (long)size); + } + if (size == 0) return 0; + return kcp->output((const char*)data, size, kcp, kcp->user); +} + +// output queue +void ikcp_qprint(const char *name, const struct IQUEUEHEAD *head) +{ +#if 0 + const struct IQUEUEHEAD *p; + printf("<%s>: [", name); + for (p = head->next; p != head; p = p->next) { + const IKCPSEG *seg = iqueue_entry(p, const IKCPSEG, node); + printf("(%lu %d)", (unsigned long)seg->sn, (int)(seg->ts % 10000)); + if (p->next != head) printf(","); + } + printf("]\n"); +#endif +} + + +//--------------------------------------------------------------------- +// create a new kcpcb +//--------------------------------------------------------------------- +ikcpcb* ikcp_create(IUINT32 conv, void *user) +{ + ikcpcb *kcp = (ikcpcb*)ikcp_malloc(sizeof(struct IKCPCB)); + if (kcp == NULL) return NULL; + kcp->conv = conv; + kcp->user = user; + kcp->snd_una = 0; + kcp->snd_nxt = 0; + kcp->rcv_nxt = 0; + kcp->ts_recent = 0; + kcp->ts_lastack = 0; + kcp->ts_probe = 0; + kcp->probe_wait = 0; + kcp->snd_wnd = IKCP_WND_SND; + kcp->rcv_wnd = IKCP_WND_RCV; + kcp->rmt_wnd = IKCP_WND_RCV; + kcp->cwnd = 0; + kcp->incr = 0; + kcp->probe = 0; + kcp->mtu = IKCP_MTU_DEF; + kcp->mss = kcp->mtu - IKCP_OVERHEAD; + kcp->stream = 0; + + kcp->buffer = (char*)ikcp_malloc((kcp->mtu + IKCP_OVERHEAD) * 3); + if (kcp->buffer == NULL) { + ikcp_free(kcp); + return NULL; + } + + iqueue_init(&kcp->snd_queue); + iqueue_init(&kcp->rcv_queue); + iqueue_init(&kcp->snd_buf); + iqueue_init(&kcp->rcv_buf); + kcp->nrcv_buf = 0; + kcp->nsnd_buf = 0; + kcp->nrcv_que = 0; + kcp->nsnd_que = 0; + kcp->state = 0; + kcp->acklist = NULL; + kcp->ackblock = 0; + kcp->ackcount = 0; + kcp->rx_srtt = 0; + kcp->rx_rttval = 0; + kcp->rx_rto = IKCP_RTO_DEF; + kcp->rx_minrto = IKCP_RTO_MIN; + kcp->current = 0; + kcp->interval = IKCP_INTERVAL; + kcp->ts_flush = IKCP_INTERVAL; + kcp->nodelay = 0; + kcp->updated = 0; + kcp->logmask = 0; + kcp->ssthresh = IKCP_THRESH_INIT; + kcp->fastresend = 0; + kcp->fastlimit = IKCP_FASTACK_LIMIT; + kcp->nocwnd = 0; + kcp->xmit = 0; + kcp->dead_link = IKCP_DEADLINK; + kcp->output = NULL; + kcp->writelog = NULL; + + return kcp; +} + + +//--------------------------------------------------------------------- +// release a new kcpcb +//--------------------------------------------------------------------- +void ikcp_release(ikcpcb *kcp) +{ + assert(kcp); + if (kcp) { + IKCPSEG *seg; + while (!iqueue_is_empty(&kcp->snd_buf)) { + seg = iqueue_entry(kcp->snd_buf.next, IKCPSEG, node); + iqueue_del(&seg->node); + ikcp_segment_delete(kcp, seg); + } + while (!iqueue_is_empty(&kcp->rcv_buf)) { + seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); + iqueue_del(&seg->node); + ikcp_segment_delete(kcp, seg); + } + while (!iqueue_is_empty(&kcp->snd_queue)) { + seg = iqueue_entry(kcp->snd_queue.next, IKCPSEG, node); + iqueue_del(&seg->node); + ikcp_segment_delete(kcp, seg); + } + while (!iqueue_is_empty(&kcp->rcv_queue)) { + seg = iqueue_entry(kcp->rcv_queue.next, IKCPSEG, node); + iqueue_del(&seg->node); + ikcp_segment_delete(kcp, seg); + } + if (kcp->buffer) { + ikcp_free(kcp->buffer); + } + if (kcp->acklist) { + ikcp_free(kcp->acklist); + } + + kcp->nrcv_buf = 0; + kcp->nsnd_buf = 0; + kcp->nrcv_que = 0; + kcp->nsnd_que = 0; + kcp->ackcount = 0; + kcp->buffer = NULL; + kcp->acklist = NULL; + ikcp_free(kcp); + } +} + + +//--------------------------------------------------------------------- +// set output callback, which will be invoked by kcp +//--------------------------------------------------------------------- +void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len, + ikcpcb *kcp, void *user)) +{ + kcp->output = output; +} + + +//--------------------------------------------------------------------- +// user/upper level recv: returns size, returns below zero for EAGAIN +//--------------------------------------------------------------------- +int ikcp_recv(ikcpcb *kcp, char *buffer, int len) +{ + struct IQUEUEHEAD *p; + int ispeek = (len < 0)? 1 : 0; + int peeksize; + int recover = 0; + IKCPSEG *seg; + assert(kcp); + + if (iqueue_is_empty(&kcp->rcv_queue)) + return -1; + + if (len < 0) len = -len; + + peeksize = ikcp_peeksize(kcp); + + if (peeksize < 0) + return -2; + + if (peeksize > len) + return -3; + + if (kcp->nrcv_que >= kcp->rcv_wnd) + recover = 1; + + // merge fragment + for (len = 0, p = kcp->rcv_queue.next; p != &kcp->rcv_queue; ) { + int fragment; + seg = iqueue_entry(p, IKCPSEG, node); + p = p->next; + + if (buffer) { + memcpy(buffer, seg->data, seg->len); + buffer += seg->len; + } + + len += seg->len; + fragment = seg->frg; + + if (ikcp_canlog(kcp, IKCP_LOG_RECV)) { + ikcp_log(kcp, IKCP_LOG_RECV, "recv sn=%lu", (unsigned long)seg->sn); + } + + if (ispeek == 0) { + iqueue_del(&seg->node); + ikcp_segment_delete(kcp, seg); + kcp->nrcv_que--; + } + + if (fragment == 0) + break; + } + + assert(len == peeksize); + + // move available data from rcv_buf -> rcv_queue + while (! iqueue_is_empty(&kcp->rcv_buf)) { + seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); + if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) { + iqueue_del(&seg->node); + kcp->nrcv_buf--; + iqueue_add_tail(&seg->node, &kcp->rcv_queue); + kcp->nrcv_que++; + kcp->rcv_nxt++; + } else { + break; + } + } + + // fast recover + if (kcp->nrcv_que < kcp->rcv_wnd && recover) { + // ready to send back IKCP_CMD_WINS in ikcp_flush + // tell remote my window size + kcp->probe |= IKCP_ASK_TELL; + } + + return len; +} + + +//--------------------------------------------------------------------- +// peek data size +//--------------------------------------------------------------------- +int ikcp_peeksize(const ikcpcb *kcp) +{ + struct IQUEUEHEAD *p; + IKCPSEG *seg; + int length = 0; + + assert(kcp); + + if (iqueue_is_empty(&kcp->rcv_queue)) return -1; + + seg = iqueue_entry(kcp->rcv_queue.next, IKCPSEG, node); + if (seg->frg == 0) return seg->len; + + if (kcp->nrcv_que < seg->frg + 1) return -1; + + for (p = kcp->rcv_queue.next; p != &kcp->rcv_queue; p = p->next) { + seg = iqueue_entry(p, IKCPSEG, node); + length += seg->len; + if (seg->frg == 0) break; + } + + return length; +} + + +//--------------------------------------------------------------------- +// user/upper level send, returns below zero for error +//--------------------------------------------------------------------- +int ikcp_send(ikcpcb *kcp, const char *buffer, int len) +{ + IKCPSEG *seg; + int count, i; + int sent = 0; + + assert(kcp->mss > 0); + if (len < 0) return -1; + + // append to previous segment in streaming mode (if possible) + if (kcp->stream != 0) { + if (!iqueue_is_empty(&kcp->snd_queue)) { + IKCPSEG *old = iqueue_entry(kcp->snd_queue.prev, IKCPSEG, node); + if (old->len < kcp->mss) { + int capacity = kcp->mss - old->len; + int extend = (len < capacity)? len : capacity; + seg = ikcp_segment_new(kcp, old->len + extend); + assert(seg); + if (seg == NULL) { + return -2; + } + iqueue_add_tail(&seg->node, &kcp->snd_queue); + memcpy(seg->data, old->data, old->len); + if (buffer) { + memcpy(seg->data + old->len, buffer, extend); + buffer += extend; + } + seg->len = old->len + extend; + seg->frg = 0; + len -= extend; + iqueue_del_init(&old->node); + ikcp_segment_delete(kcp, old); + sent = extend; + } + } + if (len <= 0) { + return sent; + } + } + + if (len <= (int)kcp->mss) count = 1; + else count = (len + kcp->mss - 1) / kcp->mss; + + if (count >= (int)IKCP_WND_RCV) { + if (kcp->stream != 0 && sent > 0) + return sent; + return -2; + } + + if (count == 0) count = 1; + + // fragment + for (i = 0; i < count; i++) { + int size = len > (int)kcp->mss ? (int)kcp->mss : len; + seg = ikcp_segment_new(kcp, size); + assert(seg); + if (seg == NULL) { + return -2; + } + if (buffer && len > 0) { + memcpy(seg->data, buffer, size); + } + seg->len = size; + seg->frg = (kcp->stream == 0)? (count - i - 1) : 0; + iqueue_init(&seg->node); + iqueue_add_tail(&seg->node, &kcp->snd_queue); + kcp->nsnd_que++; + if (buffer) { + buffer += size; + } + len -= size; + sent += size; + } + + return sent; +} + + +//--------------------------------------------------------------------- +// parse ack +//--------------------------------------------------------------------- +static void ikcp_update_ack(ikcpcb *kcp, IINT32 rtt) +{ + IINT32 rto = 0; + if (kcp->rx_srtt == 0) { + kcp->rx_srtt = rtt; + kcp->rx_rttval = rtt / 2; + } else { + long delta = rtt - kcp->rx_srtt; + if (delta < 0) delta = -delta; + kcp->rx_rttval = (3 * kcp->rx_rttval + delta) / 4; + kcp->rx_srtt = (7 * kcp->rx_srtt + rtt) / 8; + if (kcp->rx_srtt < 1) kcp->rx_srtt = 1; + } + rto = kcp->rx_srtt + _imax_(kcp->interval, 4 * kcp->rx_rttval); + kcp->rx_rto = _ibound_(kcp->rx_minrto, rto, IKCP_RTO_MAX); +} + +static void ikcp_shrink_buf(ikcpcb *kcp) +{ + struct IQUEUEHEAD *p = kcp->snd_buf.next; + if (p != &kcp->snd_buf) { + IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); + kcp->snd_una = seg->sn; + } else { + kcp->snd_una = kcp->snd_nxt; + } +} + +static void ikcp_parse_ack(ikcpcb *kcp, IUINT32 sn) +{ + struct IQUEUEHEAD *p, *next; + + if (_itimediff(sn, kcp->snd_una) < 0 || _itimediff(sn, kcp->snd_nxt) >= 0) + return; + + for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) { + IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); + next = p->next; + if (sn == seg->sn) { + iqueue_del(p); + ikcp_segment_delete(kcp, seg); + kcp->nsnd_buf--; + break; + } + if (_itimediff(sn, seg->sn) < 0) { + break; + } + } +} + +static void ikcp_parse_una(ikcpcb *kcp, IUINT32 una) +{ + struct IQUEUEHEAD *p, *next; + for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) { + IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); + next = p->next; + if (_itimediff(una, seg->sn) > 0) { + iqueue_del(p); + ikcp_segment_delete(kcp, seg); + kcp->nsnd_buf--; + } else { + break; + } + } +} + +static void ikcp_parse_fastack(ikcpcb *kcp, IUINT32 sn, IUINT32 ts) +{ + struct IQUEUEHEAD *p, *next; + + if (_itimediff(sn, kcp->snd_una) < 0 || _itimediff(sn, kcp->snd_nxt) >= 0) + return; + + for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) { + IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); + next = p->next; + if (_itimediff(sn, seg->sn) < 0) { + break; + } + else if (sn != seg->sn) { + #ifndef IKCP_FASTACK_CONSERVE + seg->fastack++; + #else + if (_itimediff(ts, seg->ts) >= 0) + seg->fastack++; + #endif + } + } +} + + +//--------------------------------------------------------------------- +// ack append +//--------------------------------------------------------------------- +static void ikcp_ack_push(ikcpcb *kcp, IUINT32 sn, IUINT32 ts) +{ + IUINT32 newsize = kcp->ackcount + 1; + IUINT32 *ptr; + + if (newsize > kcp->ackblock) { + IUINT32 *acklist; + IUINT32 newblock; + + for (newblock = 8; newblock < newsize; newblock <<= 1); + acklist = (IUINT32*)ikcp_malloc(newblock * sizeof(IUINT32) * 2); + + if (acklist == NULL) { + assert(acklist != NULL); + abort(); + } + + if (kcp->acklist != NULL) { + IUINT32 x; + for (x = 0; x < kcp->ackcount; x++) { + acklist[x * 2 + 0] = kcp->acklist[x * 2 + 0]; + acklist[x * 2 + 1] = kcp->acklist[x * 2 + 1]; + } + ikcp_free(kcp->acklist); + } + + kcp->acklist = acklist; + kcp->ackblock = newblock; + } + + ptr = &kcp->acklist[kcp->ackcount * 2]; + ptr[0] = sn; + ptr[1] = ts; + kcp->ackcount++; +} + +static void ikcp_ack_get(const ikcpcb *kcp, int p, IUINT32 *sn, IUINT32 *ts) +{ + if (sn) sn[0] = kcp->acklist[p * 2 + 0]; + if (ts) ts[0] = kcp->acklist[p * 2 + 1]; +} + + +//--------------------------------------------------------------------- +// parse data +//--------------------------------------------------------------------- +void ikcp_parse_data(ikcpcb *kcp, IKCPSEG *newseg) +{ + struct IQUEUEHEAD *p, *prev; + IUINT32 sn = newseg->sn; + int repeat = 0; + + if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) >= 0 || + _itimediff(sn, kcp->rcv_nxt) < 0) { + ikcp_segment_delete(kcp, newseg); + return; + } + + for (p = kcp->rcv_buf.prev; p != &kcp->rcv_buf; p = prev) { + IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); + prev = p->prev; + if (seg->sn == sn) { + repeat = 1; + break; + } + if (_itimediff(sn, seg->sn) > 0) { + break; + } + } + + if (repeat == 0) { + iqueue_init(&newseg->node); + iqueue_add(&newseg->node, p); + kcp->nrcv_buf++; + } else { + ikcp_segment_delete(kcp, newseg); + } + +#if 0 + ikcp_qprint("rcvbuf", &kcp->rcv_buf); + printf("rcv_nxt=%lu\n", kcp->rcv_nxt); +#endif + + // move available data from rcv_buf -> rcv_queue + while (! iqueue_is_empty(&kcp->rcv_buf)) { + IKCPSEG *seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); + if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) { + iqueue_del(&seg->node); + kcp->nrcv_buf--; + iqueue_add_tail(&seg->node, &kcp->rcv_queue); + kcp->nrcv_que++; + kcp->rcv_nxt++; + } else { + break; + } + } + +#if 0 + ikcp_qprint("queue", &kcp->rcv_queue); + printf("rcv_nxt=%lu\n", kcp->rcv_nxt); +#endif + +#if 1 +// printf("snd(buf=%d, queue=%d)\n", kcp->nsnd_buf, kcp->nsnd_que); +// printf("rcv(buf=%d, queue=%d)\n", kcp->nrcv_buf, kcp->nrcv_que); +#endif +} + + +//--------------------------------------------------------------------- +// input data +//--------------------------------------------------------------------- +int ikcp_input(ikcpcb *kcp, const char *data, long size) +{ + IUINT32 prev_una = kcp->snd_una; + IUINT32 maxack = 0, latest_ts = 0; + int flag = 0; + + if (ikcp_canlog(kcp, IKCP_LOG_INPUT)) { + ikcp_log(kcp, IKCP_LOG_INPUT, "[RI] %d bytes", (int)size); + } + + if (data == NULL || (int)size < (int)IKCP_OVERHEAD) return -1; + + while (1) { + IUINT32 ts, sn, len, una, conv; + IUINT16 wnd; + IUINT8 cmd, frg; + IKCPSEG *seg; + + if (size < (int)IKCP_OVERHEAD) break; + + data = ikcp_decode32u(data, &conv); + if (conv != kcp->conv) return -1; + + data = ikcp_decode8u(data, &cmd); + data = ikcp_decode8u(data, &frg); + data = ikcp_decode16u(data, &wnd); + data = ikcp_decode32u(data, &ts); + data = ikcp_decode32u(data, &sn); + data = ikcp_decode32u(data, &una); + data = ikcp_decode32u(data, &len); + + size -= IKCP_OVERHEAD; + + if ((long)size < (long)len || (int)len < 0) return -2; + + if (cmd != IKCP_CMD_PUSH && cmd != IKCP_CMD_ACK && + cmd != IKCP_CMD_WASK && cmd != IKCP_CMD_WINS) + return -3; + + kcp->rmt_wnd = wnd; + ikcp_parse_una(kcp, una); + ikcp_shrink_buf(kcp); + + if (cmd == IKCP_CMD_ACK) { + if (_itimediff(kcp->current, ts) >= 0) { + ikcp_update_ack(kcp, _itimediff(kcp->current, ts)); + } + ikcp_parse_ack(kcp, sn); + ikcp_shrink_buf(kcp); + if (flag == 0) { + flag = 1; + maxack = sn; + latest_ts = ts; + } else { + if (_itimediff(sn, maxack) > 0) { + #ifndef IKCP_FASTACK_CONSERVE + maxack = sn; + latest_ts = ts; + #else + if (_itimediff(ts, latest_ts) > 0) { + maxack = sn; + latest_ts = ts; + } + #endif + } + } + if (ikcp_canlog(kcp, IKCP_LOG_IN_ACK)) { + ikcp_log(kcp, IKCP_LOG_IN_ACK, + "input ack: sn=%lu rtt=%ld rto=%ld", (unsigned long)sn, + (long)_itimediff(kcp->current, ts), + (long)kcp->rx_rto); + } + } + else if (cmd == IKCP_CMD_PUSH) { + if (ikcp_canlog(kcp, IKCP_LOG_IN_DATA)) { + ikcp_log(kcp, IKCP_LOG_IN_DATA, + "input psh: sn=%lu ts=%lu", (unsigned long)sn, (unsigned long)ts); + } + if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) < 0) { + ikcp_ack_push(kcp, sn, ts); + if (_itimediff(sn, kcp->rcv_nxt) >= 0) { + seg = ikcp_segment_new(kcp, len); + seg->conv = conv; + seg->cmd = cmd; + seg->frg = frg; + seg->wnd = wnd; + seg->ts = ts; + seg->sn = sn; + seg->una = una; + seg->len = len; + + if (len > 0) { + memcpy(seg->data, data, len); + } + + ikcp_parse_data(kcp, seg); + } + } + } + else if (cmd == IKCP_CMD_WASK) { + // ready to send back IKCP_CMD_WINS in ikcp_flush + // tell remote my window size + kcp->probe |= IKCP_ASK_TELL; + if (ikcp_canlog(kcp, IKCP_LOG_IN_PROBE)) { + ikcp_log(kcp, IKCP_LOG_IN_PROBE, "input probe"); + } + } + else if (cmd == IKCP_CMD_WINS) { + // do nothing + if (ikcp_canlog(kcp, IKCP_LOG_IN_WINS)) { + ikcp_log(kcp, IKCP_LOG_IN_WINS, + "input wins: %lu", (unsigned long)(wnd)); + } + } + else { + return -3; + } + + data += len; + size -= len; + } + + if (flag != 0) { + ikcp_parse_fastack(kcp, maxack, latest_ts); + } + + if (_itimediff(kcp->snd_una, prev_una) > 0) { + if (kcp->cwnd < kcp->rmt_wnd) { + IUINT32 mss = kcp->mss; + if (kcp->cwnd < kcp->ssthresh) { + kcp->cwnd++; + kcp->incr += mss; + } else { + if (kcp->incr < mss) kcp->incr = mss; + kcp->incr += (mss * mss) / kcp->incr + (mss / 16); + if ((kcp->cwnd + 1) * mss <= kcp->incr) { + #if 1 + kcp->cwnd = (kcp->incr + mss - 1) / ((mss > 0)? mss : 1); + #else + kcp->cwnd++; + #endif + } + } + if (kcp->cwnd > kcp->rmt_wnd) { + kcp->cwnd = kcp->rmt_wnd; + kcp->incr = kcp->rmt_wnd * mss; + } + } + } + + return 0; +} + + +//--------------------------------------------------------------------- +// ikcp_encode_seg +//--------------------------------------------------------------------- +static char *ikcp_encode_seg(char *ptr, const IKCPSEG *seg) +{ + ptr = ikcp_encode32u(ptr, seg->conv); + ptr = ikcp_encode8u(ptr, (IUINT8)seg->cmd); + ptr = ikcp_encode8u(ptr, (IUINT8)seg->frg); + ptr = ikcp_encode16u(ptr, (IUINT16)seg->wnd); + ptr = ikcp_encode32u(ptr, seg->ts); + ptr = ikcp_encode32u(ptr, seg->sn); + ptr = ikcp_encode32u(ptr, seg->una); + ptr = ikcp_encode32u(ptr, seg->len); + return ptr; +} + +static int ikcp_wnd_unused(const ikcpcb *kcp) +{ + if (kcp->nrcv_que < kcp->rcv_wnd) { + return kcp->rcv_wnd - kcp->nrcv_que; + } + return 0; +} + + +//--------------------------------------------------------------------- +// ikcp_flush +//--------------------------------------------------------------------- +void ikcp_flush(ikcpcb *kcp) +{ + IUINT32 current = kcp->current; + char *buffer = kcp->buffer; + char *ptr = buffer; + int count, size, i; + IUINT32 resent, cwnd; + IUINT32 rtomin; + struct IQUEUEHEAD *p; + int change = 0; + int lost = 0; + IKCPSEG seg; + + // 'ikcp_update' haven't been called. + if (kcp->updated == 0) return; + + seg.conv = kcp->conv; + seg.cmd = IKCP_CMD_ACK; + seg.frg = 0; + seg.wnd = ikcp_wnd_unused(kcp); + seg.una = kcp->rcv_nxt; + seg.len = 0; + seg.sn = 0; + seg.ts = 0; + + // flush acknowledges + count = kcp->ackcount; + for (i = 0; i < count; i++) { + size = (int)(ptr - buffer); + if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) { + ikcp_output(kcp, buffer, size); + ptr = buffer; + } + ikcp_ack_get(kcp, i, &seg.sn, &seg.ts); + ptr = ikcp_encode_seg(ptr, &seg); + } + + kcp->ackcount = 0; + + // probe window size (if remote window size equals zero) + if (kcp->rmt_wnd == 0) { + if (kcp->probe_wait == 0) { + kcp->probe_wait = IKCP_PROBE_INIT; + kcp->ts_probe = kcp->current + kcp->probe_wait; + } + else { + if (_itimediff(kcp->current, kcp->ts_probe) >= 0) { + if (kcp->probe_wait < IKCP_PROBE_INIT) + kcp->probe_wait = IKCP_PROBE_INIT; + kcp->probe_wait += kcp->probe_wait / 2; + if (kcp->probe_wait > IKCP_PROBE_LIMIT) + kcp->probe_wait = IKCP_PROBE_LIMIT; + kcp->ts_probe = kcp->current + kcp->probe_wait; + kcp->probe |= IKCP_ASK_SEND; + } + } + } else { + kcp->ts_probe = 0; + kcp->probe_wait = 0; + } + + // flush window probing commands + if (kcp->probe & IKCP_ASK_SEND) { + seg.cmd = IKCP_CMD_WASK; + size = (int)(ptr - buffer); + if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) { + ikcp_output(kcp, buffer, size); + ptr = buffer; + } + ptr = ikcp_encode_seg(ptr, &seg); + } + + // flush window probing commands + if (kcp->probe & IKCP_ASK_TELL) { + seg.cmd = IKCP_CMD_WINS; + size = (int)(ptr - buffer); + if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) { + ikcp_output(kcp, buffer, size); + ptr = buffer; + } + ptr = ikcp_encode_seg(ptr, &seg); + } + + kcp->probe = 0; + + // calculate window size + cwnd = _imin_(kcp->snd_wnd, kcp->rmt_wnd); + if (kcp->nocwnd == 0) cwnd = _imin_(kcp->cwnd, cwnd); + + // move data from snd_queue to snd_buf + while (_itimediff(kcp->snd_nxt, kcp->snd_una + cwnd) < 0) { + IKCPSEG *newseg; + if (iqueue_is_empty(&kcp->snd_queue)) break; + + newseg = iqueue_entry(kcp->snd_queue.next, IKCPSEG, node); + + iqueue_del(&newseg->node); + iqueue_add_tail(&newseg->node, &kcp->snd_buf); + kcp->nsnd_que--; + kcp->nsnd_buf++; + + newseg->conv = kcp->conv; + newseg->cmd = IKCP_CMD_PUSH; + newseg->wnd = seg.wnd; + newseg->ts = current; + newseg->sn = kcp->snd_nxt++; + newseg->una = kcp->rcv_nxt; + newseg->resendts = current; + newseg->rto = kcp->rx_rto; + newseg->fastack = 0; + newseg->xmit = 0; + } + + // calculate resent + resent = (kcp->fastresend > 0)? (IUINT32)kcp->fastresend : 0xffffffff; + rtomin = (kcp->nodelay == 0)? (kcp->rx_rto >> 3) : 0; + + // flush data segments + for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = p->next) { + IKCPSEG *segment = iqueue_entry(p, IKCPSEG, node); + int needsend = 0; + if (segment->xmit == 0) { + needsend = 1; + segment->xmit++; + segment->rto = kcp->rx_rto; + segment->resendts = current + segment->rto + rtomin; + } + else if (_itimediff(current, segment->resendts) >= 0) { + needsend = 1; + segment->xmit++; + kcp->xmit++; + if (kcp->nodelay == 0) { + segment->rto += _imax_(segment->rto, (IUINT32)kcp->rx_rto); + } else { + IINT32 step = (kcp->nodelay < 2)? + ((IINT32)(segment->rto)) : kcp->rx_rto; + segment->rto += step / 2; + } + segment->resendts = current + segment->rto; + lost = 1; + } + else if (segment->fastack >= resent) { + if ((int)segment->xmit <= kcp->fastlimit || + kcp->fastlimit <= 0) { + needsend = 1; + segment->xmit++; + segment->fastack = 0; + segment->resendts = current + segment->rto; + change++; + } + } + + if (needsend) { + int need; + segment->ts = current; + segment->wnd = seg.wnd; + segment->una = kcp->rcv_nxt; + + size = (int)(ptr - buffer); + need = IKCP_OVERHEAD + segment->len; + + if (size + need > (int)kcp->mtu) { + ikcp_output(kcp, buffer, size); + ptr = buffer; + } + + ptr = ikcp_encode_seg(ptr, segment); + + if (segment->len > 0) { + memcpy(ptr, segment->data, segment->len); + ptr += segment->len; + } + + if (segment->xmit >= kcp->dead_link) { + kcp->state = (IUINT32)-1; + } + } + } + + // flash remain segments + size = (int)(ptr - buffer); + if (size > 0) { + ikcp_output(kcp, buffer, size); + } + + // update ssthresh + if (change) { + IUINT32 inflight = kcp->snd_nxt - kcp->snd_una; + kcp->ssthresh = inflight / 2; + if (kcp->ssthresh < IKCP_THRESH_MIN) + kcp->ssthresh = IKCP_THRESH_MIN; + kcp->cwnd = kcp->ssthresh + resent; + kcp->incr = kcp->cwnd * kcp->mss; + } + + if (lost) { + kcp->ssthresh = cwnd / 2; + if (kcp->ssthresh < IKCP_THRESH_MIN) + kcp->ssthresh = IKCP_THRESH_MIN; + kcp->cwnd = 1; + kcp->incr = kcp->mss; + } + + if (kcp->cwnd < 1) { + kcp->cwnd = 1; + kcp->incr = kcp->mss; + } +} + + +//--------------------------------------------------------------------- +// update state (call it repeatedly, every 10ms-100ms), or you can ask +// ikcp_check when to call it again (without ikcp_input/_send calling). +// 'current' - current timestamp in millisec. +//--------------------------------------------------------------------- +void ikcp_update(ikcpcb *kcp, IUINT32 current) +{ + IINT32 slap; + + kcp->current = current; + + if (kcp->updated == 0) { + kcp->updated = 1; + kcp->ts_flush = kcp->current; + } + + slap = _itimediff(kcp->current, kcp->ts_flush); + + if (slap >= 10000 || slap < -10000) { + kcp->ts_flush = kcp->current; + slap = 0; + } + + if (slap >= 0) { + kcp->ts_flush += kcp->interval; + if (_itimediff(kcp->current, kcp->ts_flush) >= 0) { + kcp->ts_flush = kcp->current + kcp->interval; + } + ikcp_flush(kcp); + } +} + + +//--------------------------------------------------------------------- +// Determine when should you invoke ikcp_update: +// returns when you should invoke ikcp_update in millisec, if there +// is no ikcp_input/_send calling. you can call ikcp_update in that +// time, instead of call update repeatly. +// Important to reduce unnacessary ikcp_update invoking. use it to +// schedule ikcp_update (eg. implementing an epoll-like mechanism, +// or optimize ikcp_update when handling massive kcp connections) +//--------------------------------------------------------------------- +IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current) +{ + IUINT32 ts_flush = kcp->ts_flush; + IINT32 tm_flush = 0x7fffffff; + IINT32 tm_packet = 0x7fffffff; + IUINT32 minimal = 0; + struct IQUEUEHEAD *p; + + if (kcp->updated == 0) { + return current; + } + + if (_itimediff(current, ts_flush) >= 10000 || + _itimediff(current, ts_flush) < -10000) { + ts_flush = current; + } + + if (_itimediff(current, ts_flush) >= 0) { + return current; + } + + tm_flush = _itimediff(ts_flush, current); + + for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = p->next) { + const IKCPSEG *seg = iqueue_entry(p, const IKCPSEG, node); + IINT32 diff = _itimediff(seg->resendts, current); + if (diff <= 0) { + return current; + } + if (diff < tm_packet) tm_packet = diff; + } + + minimal = (IUINT32)(tm_packet < tm_flush ? tm_packet : tm_flush); + if (minimal >= kcp->interval) minimal = kcp->interval; + + return current + minimal; +} + + + +int ikcp_setmtu(ikcpcb *kcp, int mtu) +{ + char *buffer; + if (mtu < 50 || mtu < (int)IKCP_OVERHEAD) + return -1; + buffer = (char*)ikcp_malloc((mtu + IKCP_OVERHEAD) * 3); + if (buffer == NULL) + return -2; + kcp->mtu = mtu; + kcp->mss = kcp->mtu - IKCP_OVERHEAD; + ikcp_free(kcp->buffer); + kcp->buffer = buffer; + return 0; +} + +int ikcp_interval(ikcpcb *kcp, int interval) +{ + if (interval > 5000) interval = 5000; + else if (interval < 10) interval = 10; + kcp->interval = interval; + return 0; +} + +int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc) +{ + if (nodelay >= 0) { + kcp->nodelay = nodelay; + if (nodelay) { + kcp->rx_minrto = IKCP_RTO_NDL; + } + else { + kcp->rx_minrto = IKCP_RTO_MIN; + } + } + if (interval >= 0) { + if (interval > 5000) interval = 5000; + else if (interval < 10) interval = 10; + kcp->interval = interval; + } + if (resend >= 0) { + kcp->fastresend = resend; + } + if (nc >= 0) { + kcp->nocwnd = nc; + } + return 0; +} + + +int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd) +{ + if (kcp) { + if (sndwnd > 0) { + kcp->snd_wnd = sndwnd; + } + if (rcvwnd > 0) { // must >= max fragment size + kcp->rcv_wnd = _imax_(rcvwnd, IKCP_WND_RCV); + } + } + return 0; +} + +int ikcp_waitsnd(const ikcpcb *kcp) +{ + return kcp->nsnd_buf + kcp->nsnd_que; +} + + +// read conv +IUINT32 ikcp_getconv(const void *ptr) +{ + IUINT32 conv; + ikcp_decode32u((const char*)ptr, &conv); + return conv; +} + + diff --git a/src/qos/kcp/ikcp.h b/src/qos/kcp/ikcp.h new file mode 100644 index 0000000..e525105 --- /dev/null +++ b/src/qos/kcp/ikcp.h @@ -0,0 +1,416 @@ +//===================================================================== +// +// KCP - A Better ARQ Protocol Implementation +// skywind3000 (at) gmail.com, 2010-2011 +// +// Features: +// + Average RTT reduce 30% - 40% vs traditional ARQ like tcp. +// + Maximum RTT reduce three times vs tcp. +// + Lightweight, distributed as a single source file. +// +//===================================================================== +#ifndef __IKCP_H__ +#define __IKCP_H__ + +#include +#include +#include + + +//===================================================================== +// 32BIT INTEGER DEFINITION +//===================================================================== +#ifndef __INTEGER_32_BITS__ +#define __INTEGER_32_BITS__ +#if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \ + defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \ + defined(_M_AMD64) + typedef unsigned int ISTDUINT32; + typedef int ISTDINT32; +#elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \ + defined(__i386) || defined(_M_X86) + typedef unsigned long ISTDUINT32; + typedef long ISTDINT32; +#elif defined(__MACOS__) + typedef UInt32 ISTDUINT32; + typedef SInt32 ISTDINT32; +#elif defined(__APPLE__) && defined(__MACH__) + #include + typedef u_int32_t ISTDUINT32; + typedef int32_t ISTDINT32; +#elif defined(__BEOS__) + #include + typedef u_int32_t ISTDUINT32; + typedef int32_t ISTDINT32; +#elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__)) + typedef unsigned __int32 ISTDUINT32; + typedef __int32 ISTDINT32; +#elif defined(__GNUC__) + #include + typedef uint32_t ISTDUINT32; + typedef int32_t ISTDINT32; +#else + typedef unsigned long ISTDUINT32; + typedef long ISTDINT32; +#endif +#endif + + +//===================================================================== +// Integer Definition +//===================================================================== +#ifndef __IINT8_DEFINED +#define __IINT8_DEFINED +typedef char IINT8; +#endif + +#ifndef __IUINT8_DEFINED +#define __IUINT8_DEFINED +typedef unsigned char IUINT8; +#endif + +#ifndef __IUINT16_DEFINED +#define __IUINT16_DEFINED +typedef unsigned short IUINT16; +#endif + +#ifndef __IINT16_DEFINED +#define __IINT16_DEFINED +typedef short IINT16; +#endif + +#ifndef __IINT32_DEFINED +#define __IINT32_DEFINED +typedef ISTDINT32 IINT32; +#endif + +#ifndef __IUINT32_DEFINED +#define __IUINT32_DEFINED +typedef ISTDUINT32 IUINT32; +#endif + +#ifndef __IINT64_DEFINED +#define __IINT64_DEFINED +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 IINT64; +#else +typedef long long IINT64; +#endif +#endif + +#ifndef __IUINT64_DEFINED +#define __IUINT64_DEFINED +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 IUINT64; +#else +typedef unsigned long long IUINT64; +#endif +#endif + +#ifndef INLINE +#if defined(__GNUC__) + +#if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) +#define INLINE __inline__ __attribute__((always_inline)) +#else +#define INLINE __inline__ +#endif + +#elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__)) +#define INLINE __inline +#else +#define INLINE +#endif +#endif + +#if (!defined(__cplusplus)) && (!defined(inline)) +#define inline INLINE +#endif + + +//===================================================================== +// QUEUE DEFINITION +//===================================================================== +#ifndef __IQUEUE_DEF__ +#define __IQUEUE_DEF__ + +struct IQUEUEHEAD { + struct IQUEUEHEAD *next, *prev; +}; + +typedef struct IQUEUEHEAD iqueue_head; + + +//--------------------------------------------------------------------- +// queue init +//--------------------------------------------------------------------- +#define IQUEUE_HEAD_INIT(name) { &(name), &(name) } +#define IQUEUE_HEAD(name) \ + struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name) + +#define IQUEUE_INIT(ptr) ( \ + (ptr)->next = (ptr), (ptr)->prev = (ptr)) + +#define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define ICONTAINEROF(ptr, type, member) ( \ + (type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) ) + +#define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member) + + +//--------------------------------------------------------------------- +// queue operation +//--------------------------------------------------------------------- +#define IQUEUE_ADD(node, head) ( \ + (node)->prev = (head), (node)->next = (head)->next, \ + (head)->next->prev = (node), (head)->next = (node)) + +#define IQUEUE_ADD_TAIL(node, head) ( \ + (node)->prev = (head)->prev, (node)->next = (head), \ + (head)->prev->next = (node), (head)->prev = (node)) + +#define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n)) + +#define IQUEUE_DEL(entry) (\ + (entry)->next->prev = (entry)->prev, \ + (entry)->prev->next = (entry)->next, \ + (entry)->next = 0, (entry)->prev = 0) + +#define IQUEUE_DEL_INIT(entry) do { \ + IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0) + +#define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next) + +#define iqueue_init IQUEUE_INIT +#define iqueue_entry IQUEUE_ENTRY +#define iqueue_add IQUEUE_ADD +#define iqueue_add_tail IQUEUE_ADD_TAIL +#define iqueue_del IQUEUE_DEL +#define iqueue_del_init IQUEUE_DEL_INIT +#define iqueue_is_empty IQUEUE_IS_EMPTY + +#define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \ + for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \ + &((iterator)->MEMBER) != (head); \ + (iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER)) + +#define iqueue_foreach(iterator, head, TYPE, MEMBER) \ + IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) + +#define iqueue_foreach_entry(pos, head) \ + for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next ) + + +#define __iqueue_splice(list, head) do { \ + iqueue_head *first = (list)->next, *last = (list)->prev; \ + iqueue_head *at = (head)->next; \ + (first)->prev = (head), (head)->next = (first); \ + (last)->next = (at), (at)->prev = (last); } while (0) + +#define iqueue_splice(list, head) do { \ + if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0) + +#define iqueue_splice_init(list, head) do { \ + iqueue_splice(list, head); iqueue_init(list); } while (0) + + +#ifdef _MSC_VER +#pragma warning(disable:4311) +#pragma warning(disable:4312) +#pragma warning(disable:4996) +#endif + +#endif + + +//--------------------------------------------------------------------- +// BYTE ORDER & ALIGNMENT +//--------------------------------------------------------------------- +#ifndef IWORDS_BIG_ENDIAN + #ifdef _BIG_ENDIAN_ + #if _BIG_ENDIAN_ + #define IWORDS_BIG_ENDIAN 1 + #endif + #endif + #ifndef IWORDS_BIG_ENDIAN + #if defined(__hppa__) || \ + defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ + (defined(__MIPS__) && defined(__MIPSEB__)) || \ + defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ + defined(__sparc__) || defined(__powerpc__) || \ + defined(__mc68000__) || defined(__s390x__) || defined(__s390__) + #define IWORDS_BIG_ENDIAN 1 + #endif + #endif + #ifndef IWORDS_BIG_ENDIAN + #define IWORDS_BIG_ENDIAN 0 + #endif +#endif + +#ifndef IWORDS_MUST_ALIGN + #if defined(__i386__) || defined(__i386) || defined(_i386_) + #define IWORDS_MUST_ALIGN 0 + #elif defined(_M_IX86) || defined(_X86_) || defined(__x86_64__) + #define IWORDS_MUST_ALIGN 0 + #elif defined(__amd64) || defined(__amd64__) + #define IWORDS_MUST_ALIGN 0 + #else + #define IWORDS_MUST_ALIGN 1 + #endif +#endif + + +//===================================================================== +// SEGMENT +//===================================================================== +struct IKCPSEG +{ + struct IQUEUEHEAD node; + IUINT32 conv; + IUINT32 cmd; + IUINT32 frg; + IUINT32 wnd; + IUINT32 ts; + IUINT32 sn; + IUINT32 una; + IUINT32 len; + IUINT32 resendts; + IUINT32 rto; + IUINT32 fastack; + IUINT32 xmit; + char data[1]; +}; + + +//--------------------------------------------------------------------- +// IKCPCB +//--------------------------------------------------------------------- +struct IKCPCB +{ + IUINT32 conv, mtu, mss, state; + IUINT32 snd_una, snd_nxt, rcv_nxt; + IUINT32 ts_recent, ts_lastack, ssthresh; + IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto; + IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe; + IUINT32 current, interval, ts_flush, xmit; + IUINT32 nrcv_buf, nsnd_buf; + IUINT32 nrcv_que, nsnd_que; + IUINT32 nodelay, updated; + IUINT32 ts_probe, probe_wait; + IUINT32 dead_link, incr; + struct IQUEUEHEAD snd_queue; + struct IQUEUEHEAD rcv_queue; + struct IQUEUEHEAD snd_buf; + struct IQUEUEHEAD rcv_buf; + IUINT32 *acklist; + IUINT32 ackcount; + IUINT32 ackblock; + void *user; + char *buffer; + int fastresend; + int fastlimit; + int nocwnd, stream; + int logmask; + int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user); + void (*writelog)(const char *log, struct IKCPCB *kcp, void *user); +}; + + +typedef struct IKCPCB ikcpcb; + +#define IKCP_LOG_OUTPUT 1 +#define IKCP_LOG_INPUT 2 +#define IKCP_LOG_SEND 4 +#define IKCP_LOG_RECV 8 +#define IKCP_LOG_IN_DATA 16 +#define IKCP_LOG_IN_ACK 32 +#define IKCP_LOG_IN_PROBE 64 +#define IKCP_LOG_IN_WINS 128 +#define IKCP_LOG_OUT_DATA 256 +#define IKCP_LOG_OUT_ACK 512 +#define IKCP_LOG_OUT_PROBE 1024 +#define IKCP_LOG_OUT_WINS 2048 + +#ifdef __cplusplus +extern "C" { +#endif + +//--------------------------------------------------------------------- +// interface +//--------------------------------------------------------------------- + +// create a new kcp control object, 'conv' must equal in two endpoint +// from the same connection. 'user' will be passed to the output callback +// output callback can be setup like this: 'kcp->output = my_udp_output' +ikcpcb* ikcp_create(IUINT32 conv, void *user); + +// release kcp control object +void ikcp_release(ikcpcb *kcp); + +// set output callback, which will be invoked by kcp +void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len, + ikcpcb *kcp, void *user)); + +// user/upper level recv: returns size, returns below zero for EAGAIN +int ikcp_recv(ikcpcb *kcp, char *buffer, int len); + +// user/upper level send, returns below zero for error +int ikcp_send(ikcpcb *kcp, const char *buffer, int len); + +// update state (call it repeatedly, every 10ms-100ms), or you can ask +// ikcp_check when to call it again (without ikcp_input/_send calling). +// 'current' - current timestamp in millisec. +void ikcp_update(ikcpcb *kcp, IUINT32 current); + +// Determine when should you invoke ikcp_update: +// returns when you should invoke ikcp_update in millisec, if there +// is no ikcp_input/_send calling. you can call ikcp_update in that +// time, instead of call update repeatly. +// Important to reduce unnacessary ikcp_update invoking. use it to +// schedule ikcp_update (eg. implementing an epoll-like mechanism, +// or optimize ikcp_update when handling massive kcp connections) +IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current); + +// when you received a low level packet (eg. UDP packet), call it +int ikcp_input(ikcpcb *kcp, const char *data, long size); + +// flush pending data +void ikcp_flush(ikcpcb *kcp); + +// check the size of next message in the recv queue +int ikcp_peeksize(const ikcpcb *kcp); + +// change MTU size, default is 1400 +int ikcp_setmtu(ikcpcb *kcp, int mtu); + +// set maximum window size: sndwnd=32, rcvwnd=32 by default +int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd); + +// get how many packet is waiting to be sent +int ikcp_waitsnd(const ikcpcb *kcp); + +// fastest: ikcp_nodelay(kcp, 1, 20, 2, 1) +// nodelay: 0:disable(default), 1:enable +// interval: internal update timer interval in millisec, default is 100ms +// resend: 0:disable fast resend(default), 1:enable fast resend +// nc: 0:normal congestion control(default), 1:disable congestion control +int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc); + + +void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...); + +// setup allocator +void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*)); + +// read conv +IUINT32 ikcp_getconv(const void *ptr); + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/src/rtc/x_inner.cpp b/src/rtc/x_inner.cpp index 5e1365f..fa0cc61 100644 --- a/src/rtc/x_inner.cpp +++ b/src/rtc/x_inner.cpp @@ -41,8 +41,15 @@ int JoinConnection(PeerPtr *peer_ptr, const char *transmission_id, return 0; } -int SendData(PeerPtr *peer_ptr, const char *data, size_t size) { - peer_ptr->peer_connection->SendData(data, size); +int SendData(PeerPtr *peer_ptr, DATA_TYPE data_type, const char *data, + size_t size) { + if (DATA_TYPE::VIDEO == data_type) { + peer_ptr->peer_connection->SendVideoData(data, size); + } else if (DATA_TYPE::AUDIO == data_type) { + peer_ptr->peer_connection->SendAudioData(data, size); + } else if (DATA_TYPE::USER == data_type) { + peer_ptr->peer_connection->SendUserData(data, size); + } return 0; } diff --git a/src/transmission/ice_transmission.cpp b/src/transmission/ice_transmission.cpp index 9e09a1f..464f3e0 100644 --- a/src/transmission/ice_transmission.cpp +++ b/src/transmission/ice_transmission.cpp @@ -1,5 +1,6 @@ #include "ice_transmission.h" +#include #include #include #include @@ -7,7 +8,57 @@ #include "common.h" #include "log.h" +#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) +#include +#elif !defined(__unix) +#define __unix +#endif + +#ifdef __unix +#include +#include +#include +#include +#endif + using nlohmann::json; +static int count = 1; + +static inline void itimeofday(long *sec, long *usec) { +#if defined(__unix) + struct timeval time; + gettimeofday(&time, NULL); + if (sec) *sec = time.tv_sec; + if (usec) *usec = time.tv_usec; +#else + static long mode = 0, addsec = 0; + BOOL retval; + static IINT64 freq = 1; + IINT64 qpc; + if (mode == 0) { + retval = QueryPerformanceFrequency((LARGE_INTEGER *)&freq); + freq = (freq == 0) ? 1 : freq; + retval = QueryPerformanceCounter((LARGE_INTEGER *)&qpc); + addsec = (long)time(NULL); + addsec = addsec - (long)((qpc / freq) & 0x7fffffff); + mode = 1; + } + retval = QueryPerformanceCounter((LARGE_INTEGER *)&qpc); + retval = retval * 2; + if (sec) *sec = (long)(qpc / freq) + addsec; + if (usec) *usec = (long)((qpc % freq) * 1000000 / freq); +#endif +} + +static inline IINT64 iclock64(void) { + long s, u; + IINT64 value; + itimeofday(&s, &u); + value = ((IINT64)s) * 1000 + (u / 1000); + return value; +} + +static inline IUINT32 iclock() { return (IUINT32)(iclock64() & 0xfffffffful); } const std::vector ice_status = { "JUICE_STATE_DISCONNECTED", "JUICE_STATE_GATHERING", @@ -31,9 +82,45 @@ IceTransmission::~IceTransmission() { delete ice_agent_; ice_agent_ = nullptr; } + ikcp_release(kcp_); } int IceTransmission::InitIceTransmission(std::string &ip, int port) { + kcp_ = ikcp_create(0x11223344, (void *)this); + ikcp_setoutput(kcp_, + [](const char *buf, int len, ikcpcb *kcp, void *user) -> int { + IceTransmission *ice_transmission_obj = + static_cast(user); + LOG_ERROR("Real send size: {}", len); + return ice_transmission_obj->ice_agent_->Send(buf, len); + }); + // ikcp_wndsize(kcp_, 1280, 1280); + ikcp_nodelay(kcp_, 0, 40, 0, 0); + ikcp_setmtu(kcp_, 4000); + // kcp_->rx_minrto = 10; + // kcp_->fastresend = 1; + std::thread kcp_update_thread([this]() { + while (1) { + auto clock = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + mtx_.lock(); + ikcp_update(kcp_, iclock()); + + int len = 0; + int total_len = 0; + while (1) { + len = ikcp_recv(kcp_, kcp_complete_buffer_ + len, 1400); + total_len += len; + if (len <= 0) break; + } + + mtx_.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + }); + kcp_update_thread.detach(); + ice_agent_ = new IceAgent(ip, port); ice_agent_->CreateIceAgent( @@ -43,6 +130,7 @@ int IceTransmission::InitIceTransmission(std::string &ip, int port) { static_cast(user_ptr); LOG_INFO("[{}->{}] state_change: {}", ice_transmission_obj->user_id_, ice_transmission_obj->remote_user_id_, ice_status[state]); + ice_transmission_obj->state_ = state; } else { LOG_INFO("state_change: {}", ice_status[state]); } @@ -74,9 +162,28 @@ int IceTransmission::InitIceTransmission(std::string &ip, int port) { IceTransmission *ice_transmission_obj = static_cast(user_ptr); if (ice_transmission_obj->on_receive_ice_msg_cb_) { - ice_transmission_obj->on_receive_ice_msg_cb_( - data, size, ice_transmission_obj->remote_user_id_.data(), - ice_transmission_obj->remote_user_id_.size()); + LOG_ERROR("[{}] Receive size: {}", (void *)user_ptr, size); + ice_transmission_obj->mtx_.lock(); + int ret = ikcp_input(ice_transmission_obj->kcp_, data, size); + // ikcp_update(ice_transmission_obj->kcp_, iclock()); + LOG_ERROR("ikcp_input {}", ret); + // auto clock = + // std::chrono::duration_cast( + // std::chrono::system_clock::now().time_since_epoch()) + // .count(); + + // ikcp_update(ice_transmission_obj->kcp_, clock); + + ice_transmission_obj->mtx_.unlock(); + + // ice_transmission_obj->on_receive_ice_msg_cb_( + // ice_transmission_obj->kcp_complete_buffer_, total_len, + // ice_transmission_obj->remote_user_id_.data(), + // ice_transmission_obj->remote_user_id_.size()); + + // ice_transmission_obj->on_receive_ice_msg_cb_( + // data, size, ice_transmission_obj->remote_user_id_.data(), + // ice_transmission_obj->remote_user_id_.size()); } } }, @@ -167,6 +274,21 @@ int IceTransmission::SendAnswer() { } int IceTransmission::SendData(const char *data, size_t size) { - ice_agent_->Send(data, size); + if (JUICE_STATE_COMPLETED == state_) { + LOG_ERROR("[{}] Wanna send size: {}", (void *)this, size); + mtx_.lock(); + + if (ikcp_waitsnd(kcp_) > kcp_->snd_wnd) { + // LOG_ERROR("Skip frame"); + // mtx_.unlock(); + // return 0; + ikcp_flush(kcp_); + } + int ret = ikcp_send(kcp_, data, size / 100); + LOG_ERROR("ikcp_send {}, wnd [{} | {}]", ret, ikcp_waitsnd(kcp_), + kcp_->snd_wnd); + mtx_.unlock(); + // ice_agent_->Send(data, size); + } return 0; } \ No newline at end of file diff --git a/src/transmission/ice_transmission.h b/src/transmission/ice_transmission.h index f4c41cf..974a485 100644 --- a/src/transmission/ice_transmission.h +++ b/src/transmission/ice_transmission.h @@ -5,8 +5,8 @@ #include "congestion_control.h" #include "ice_agent.h" +#include "ikcp.h" #include "ws_transmission.h" - class IceTransmission { public: IceTransmission( @@ -61,6 +61,12 @@ class IceTransmission { std::string remote_user_id_ = ""; bool offer_peer_ = true; std::string remote_ice_username_ = ""; + juice_state_t state_ = JUICE_STATE_DISCONNECTED; + + private: + ikcpcb *kcp_ = nullptr; + char kcp_complete_buffer_[2560 * 1440 * 4]; + std::mutex mtx_; }; #endif \ No newline at end of file diff --git a/tests/peerconnection/guest.cpp b/tests/peerconnection/guest.cpp index 56c96a5..e220f58 100644 --- a/tests/peerconnection/guest.cpp +++ b/tests/peerconnection/guest.cpp @@ -4,10 +4,10 @@ void GuestReceiveBuffer(const char* data, size_t size, const char* user_id, size_t user_id_size) { - std::string msg(data, size); - std::string user(user_id, user_id_size); + // std::string msg(data, size); + // std::string user(user_id, user_id_size); - std::cout << "Receive: [" << user << "] " << msg << std::endl; + // std::cout << "Receive: [" << user << "] " << msg << std::endl; } int main(int argc, char** argv) { @@ -26,12 +26,15 @@ int main(int argc, char** argv) { std::string msg = "Hello world"; int i = 100; - while (i--) { - getchar(); - std::cout << "Send msg: " << msg << std::endl; - SendData(peer, msg.data(), msg.size()); - } + // while (i--) { + // getchar(); + // std::cout << "Send msg: " << msg << std::endl; + // SendData(peer, DATA_TYPE::USER, msg.data(), msg.size()); + // } - getchar(); + // getchar(); + + while (1) { + } return 0; } diff --git a/tests/peerconnection/host.cpp b/tests/peerconnection/host.cpp index a488d2e..985aaab 100644 --- a/tests/peerconnection/host.cpp +++ b/tests/peerconnection/host.cpp @@ -22,13 +22,16 @@ int main(int argc, char** argv) { std::string msg = "Hello world"; - int i = 100; - while (i--) { - getchar(); - std::cout << "Send msg: " << msg << std::endl; - SendData(peer, msg.data(), msg.size()); + // int i = 100; + // while (i--) { + // getchar(); + // std::cout << "Send msg: " << msg << std::endl; + // SendData(peer, DATA_TYPE::USER, msg.data(), msg.size()); + // } + + // getchar(); + while (1) { } - getchar(); return 0; } diff --git a/thirdparty/ffmpeg/xmake.lua b/thirdparty/ffmpeg/xmake.lua index 7290ee8..e08c18b 100644 --- a/thirdparty/ffmpeg/xmake.lua +++ b/thirdparty/ffmpeg/xmake.lua @@ -32,7 +32,7 @@ package("ffmpeg") add_configs("hardcoded-tables", {description = "Enable hardcoded tables.", default = true, type = "boolean"}) end - add_links("avfilter", "avdevice", "avformat", "avcodec", "swscale", "swresample", "avutil") + -- add_links("avfilter", "avdevice", "avformat", "avcodec", "swscale", "swresample", "avutil") if is_plat("macosx") then add_frameworks("CoreFoundation", "Foundation", "CoreVideo", "CoreMedia", "AudioToolbox", "VideoToolbox", "Security") elseif is_plat("linux") then diff --git a/thirdparty/nvcodec/Interface/cuviddec.h b/thirdparty/nvcodec/Interface/cuviddec.h new file mode 100644 index 0000000..e9cac9e --- /dev/null +++ b/thirdparty/nvcodec/Interface/cuviddec.h @@ -0,0 +1,1190 @@ +/* + * This copyright notice applies to this header file only: + * + * Copyright (c) 2010-2020 NVIDIA Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the software, and to permit persons to whom the + * software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*****************************************************************************************************/ +//! \file cuviddec.h +//! NVDECODE API provides video decoding interface to NVIDIA GPU devices. +//! This file contains constants, structure definitions and function prototypes used for decoding. +/*****************************************************************************************************/ + +#if !defined(__CUDA_VIDEO_H__) +#define __CUDA_VIDEO_H__ + +#ifndef __cuda_cuda_h__ +#include +#endif // __cuda_cuda_h__ + +#if defined(_WIN64) || defined(__LP64__) || defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) +#if (CUDA_VERSION >= 3020) && (!defined(CUDA_FORCE_API_VERSION) || (CUDA_FORCE_API_VERSION >= 3020)) +#define __CUVID_DEVPTR64 +#endif +#endif + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ + +typedef void *CUvideodecoder; +typedef struct _CUcontextlock_st *CUvideoctxlock; + +/*********************************************************************************/ +//! \enum cudaVideoCodec +//! Video codec enums +//! These enums are used in CUVIDDECODECREATEINFO and CUVIDDECODECAPS structures +/*********************************************************************************/ +typedef enum cudaVideoCodec_enum { + cudaVideoCodec_MPEG1=0, /**< MPEG1 */ + cudaVideoCodec_MPEG2, /**< MPEG2 */ + cudaVideoCodec_MPEG4, /**< MPEG4 */ + cudaVideoCodec_VC1, /**< VC1 */ + cudaVideoCodec_H264, /**< H264 */ + cudaVideoCodec_JPEG, /**< JPEG */ + cudaVideoCodec_H264_SVC, /**< H264-SVC */ + cudaVideoCodec_H264_MVC, /**< H264-MVC */ + cudaVideoCodec_HEVC, /**< HEVC */ + cudaVideoCodec_VP8, /**< VP8 */ + cudaVideoCodec_VP9, /**< VP9 */ + cudaVideoCodec_AV1, /**< AV1 */ + cudaVideoCodec_NumCodecs, /**< Max codecs */ + // Uncompressed YUV + cudaVideoCodec_YUV420 = (('I'<<24)|('Y'<<16)|('U'<<8)|('V')), /**< Y,U,V (4:2:0) */ + cudaVideoCodec_YV12 = (('Y'<<24)|('V'<<16)|('1'<<8)|('2')), /**< Y,V,U (4:2:0) */ + cudaVideoCodec_NV12 = (('N'<<24)|('V'<<16)|('1'<<8)|('2')), /**< Y,UV (4:2:0) */ + cudaVideoCodec_YUYV = (('Y'<<24)|('U'<<16)|('Y'<<8)|('V')), /**< YUYV/YUY2 (4:2:2) */ + cudaVideoCodec_UYVY = (('U'<<24)|('Y'<<16)|('V'<<8)|('Y')) /**< UYVY (4:2:2) */ +} cudaVideoCodec; + +/*********************************************************************************/ +//! \enum cudaVideoSurfaceFormat +//! Video surface format enums used for output format of decoded output +//! These enums are used in CUVIDDECODECREATEINFO structure +/*********************************************************************************/ +typedef enum cudaVideoSurfaceFormat_enum { + cudaVideoSurfaceFormat_NV12=0, /**< Semi-Planar YUV [Y plane followed by interleaved UV plane] */ + cudaVideoSurfaceFormat_P016=1, /**< 16 bit Semi-Planar YUV [Y plane followed by interleaved UV plane]. + Can be used for 10 bit(6LSB bits 0), 12 bit (4LSB bits 0) */ + cudaVideoSurfaceFormat_YUV444=2, /**< Planar YUV [Y plane followed by U and V planes] */ + cudaVideoSurfaceFormat_YUV444_16Bit=3, /**< 16 bit Planar YUV [Y plane followed by U and V planes]. + Can be used for 10 bit(6LSB bits 0), 12 bit (4LSB bits 0) */ +} cudaVideoSurfaceFormat; + +/******************************************************************************************************************/ +//! \enum cudaVideoDeinterlaceMode +//! Deinterlacing mode enums +//! These enums are used in CUVIDDECODECREATEINFO structure +//! Use cudaVideoDeinterlaceMode_Weave for progressive content and for content that doesn't need deinterlacing +//! cudaVideoDeinterlaceMode_Adaptive needs more video memory than other DImodes +/******************************************************************************************************************/ +typedef enum cudaVideoDeinterlaceMode_enum { + cudaVideoDeinterlaceMode_Weave=0, /**< Weave both fields (no deinterlacing) */ + cudaVideoDeinterlaceMode_Bob, /**< Drop one field */ + cudaVideoDeinterlaceMode_Adaptive /**< Adaptive deinterlacing */ +} cudaVideoDeinterlaceMode; + +/**************************************************************************************************************/ +//! \enum cudaVideoChromaFormat +//! Chroma format enums +//! These enums are used in CUVIDDECODECREATEINFO and CUVIDDECODECAPS structures +/**************************************************************************************************************/ +typedef enum cudaVideoChromaFormat_enum { + cudaVideoChromaFormat_Monochrome=0, /**< MonoChrome */ + cudaVideoChromaFormat_420, /**< YUV 4:2:0 */ + cudaVideoChromaFormat_422, /**< YUV 4:2:2 */ + cudaVideoChromaFormat_444 /**< YUV 4:4:4 */ +} cudaVideoChromaFormat; + +/*************************************************************************************************************/ +//! \enum cudaVideoCreateFlags +//! Decoder flag enums to select preferred decode path +//! cudaVideoCreate_Default and cudaVideoCreate_PreferCUVID are most optimized, use these whenever possible +/*************************************************************************************************************/ +typedef enum cudaVideoCreateFlags_enum { + cudaVideoCreate_Default = 0x00, /**< Default operation mode: use dedicated video engines */ + cudaVideoCreate_PreferCUDA = 0x01, /**< Use CUDA-based decoder (requires valid vidLock object for multi-threading) */ + cudaVideoCreate_PreferDXVA = 0x02, /**< Go through DXVA internally if possible (requires D3D9 interop) */ + cudaVideoCreate_PreferCUVID = 0x04 /**< Use dedicated video engines directly */ +} cudaVideoCreateFlags; + + +/*************************************************************************/ +//! \enum cuvidDecodeStatus +//! Decode status enums +//! These enums are used in CUVIDGETDECODESTATUS structure +/*************************************************************************/ +typedef enum cuvidDecodeStatus_enum +{ + cuvidDecodeStatus_Invalid = 0, // Decode status is not valid + cuvidDecodeStatus_InProgress = 1, // Decode is in progress + cuvidDecodeStatus_Success = 2, // Decode is completed without any errors + // 3 to 7 enums are reserved for future use + cuvidDecodeStatus_Error = 8, // Decode is completed with an error (error is not concealed) + cuvidDecodeStatus_Error_Concealed = 9, // Decode is completed with an error and error is concealed +} cuvidDecodeStatus; + +/**************************************************************************************************************/ +//! \struct CUVIDDECODECAPS; +//! This structure is used in cuvidGetDecoderCaps API +/**************************************************************************************************************/ +typedef struct _CUVIDDECODECAPS +{ + cudaVideoCodec eCodecType; /**< IN: cudaVideoCodec_XXX */ + cudaVideoChromaFormat eChromaFormat; /**< IN: cudaVideoChromaFormat_XXX */ + unsigned int nBitDepthMinus8; /**< IN: The Value "BitDepth minus 8" */ + unsigned int reserved1[3]; /**< Reserved for future use - set to zero */ + + unsigned char bIsSupported; /**< OUT: 1 if codec supported, 0 if not supported */ + unsigned char nNumNVDECs; /**< OUT: Number of NVDECs that can support IN params */ + unsigned short nOutputFormatMask; /**< OUT: each bit represents corresponding cudaVideoSurfaceFormat enum */ + unsigned int nMaxWidth; /**< OUT: Max supported coded width in pixels */ + unsigned int nMaxHeight; /**< OUT: Max supported coded height in pixels */ + unsigned int nMaxMBCount; /**< OUT: Max supported macroblock count + CodedWidth*CodedHeight/256 must be <= nMaxMBCount */ + unsigned short nMinWidth; /**< OUT: Min supported coded width in pixels */ + unsigned short nMinHeight; /**< OUT: Min supported coded height in pixels */ + unsigned char bIsHistogramSupported; /**< OUT: 1 if Y component histogram output is supported, 0 if not + Note: histogram is computed on original picture data before + any post-processing like scaling, cropping, etc. is applied */ + unsigned char nCounterBitDepth; /**< OUT: histogram counter bit depth */ + unsigned short nMaxHistogramBins; /**< OUT: Max number of histogram bins */ + unsigned int reserved3[10]; /**< Reserved for future use - set to zero */ +} CUVIDDECODECAPS; + +/**************************************************************************************************************/ +//! \struct CUVIDDECODECREATEINFO +//! This structure is used in cuvidCreateDecoder API +/**************************************************************************************************************/ +typedef struct _CUVIDDECODECREATEINFO +{ + unsigned long ulWidth; /**< IN: Coded sequence width in pixels */ + unsigned long ulHeight; /**< IN: Coded sequence height in pixels */ + unsigned long ulNumDecodeSurfaces; /**< IN: Maximum number of internal decode surfaces */ + cudaVideoCodec CodecType; /**< IN: cudaVideoCodec_XXX */ + cudaVideoChromaFormat ChromaFormat; /**< IN: cudaVideoChromaFormat_XXX */ + unsigned long ulCreationFlags; /**< IN: Decoder creation flags (cudaVideoCreateFlags_XXX) */ + unsigned long bitDepthMinus8; /**< IN: The value "BitDepth minus 8" */ + unsigned long ulIntraDecodeOnly; /**< IN: Set 1 only if video has all intra frames (default value is 0). This will + optimize video memory for Intra frames only decoding. The support is limited + to specific codecs - H264, HEVC, VP9, the flag will be ignored for codecs which + are not supported. However decoding might fail if the flag is enabled in case + of supported codecs for regular bit streams having P and/or B frames. */ + unsigned long ulMaxWidth; /**< IN: Coded sequence max width in pixels used with reconfigure Decoder */ + unsigned long ulMaxHeight; /**< IN: Coded sequence max height in pixels used with reconfigure Decoder */ + unsigned long Reserved1; /**< Reserved for future use - set to zero */ + /** + * IN: area of the frame that should be displayed + */ + struct { + short left; + short top; + short right; + short bottom; + } display_area; + + cudaVideoSurfaceFormat OutputFormat; /**< IN: cudaVideoSurfaceFormat_XXX */ + cudaVideoDeinterlaceMode DeinterlaceMode; /**< IN: cudaVideoDeinterlaceMode_XXX */ + unsigned long ulTargetWidth; /**< IN: Post-processed output width (Should be aligned to 2) */ + unsigned long ulTargetHeight; /**< IN: Post-processed output height (Should be aligned to 2) */ + unsigned long ulNumOutputSurfaces; /**< IN: Maximum number of output surfaces simultaneously mapped */ + CUvideoctxlock vidLock; /**< IN: If non-NULL, context lock used for synchronizing ownership of + the cuda context. Needed for cudaVideoCreate_PreferCUDA decode */ + /** + * IN: target rectangle in the output frame (for aspect ratio conversion) + * if a null rectangle is specified, {0,0,ulTargetWidth,ulTargetHeight} will be used + */ + struct { + short left; + short top; + short right; + short bottom; + } target_rect; + + unsigned long enableHistogram; /**< IN: enable histogram output, if supported */ + unsigned long Reserved2[4]; /**< Reserved for future use - set to zero */ +} CUVIDDECODECREATEINFO; + +/*********************************************************/ +//! \struct CUVIDH264DPBENTRY +//! H.264 DPB entry +//! This structure is used in CUVIDH264PICPARAMS structure +/*********************************************************/ +typedef struct _CUVIDH264DPBENTRY +{ + int PicIdx; /**< picture index of reference frame */ + int FrameIdx; /**< frame_num(short-term) or LongTermFrameIdx(long-term) */ + int is_long_term; /**< 0=short term reference, 1=long term reference */ + int not_existing; /**< non-existing reference frame (corresponding PicIdx should be set to -1) */ + int used_for_reference; /**< 0=unused, 1=top_field, 2=bottom_field, 3=both_fields */ + int FieldOrderCnt[2]; /**< field order count of top and bottom fields */ +} CUVIDH264DPBENTRY; + +/************************************************************/ +//! \struct CUVIDH264MVCEXT +//! H.264 MVC picture parameters ext +//! This structure is used in CUVIDH264PICPARAMS structure +/************************************************************/ +typedef struct _CUVIDH264MVCEXT +{ + int num_views_minus1; /**< Max number of coded views minus 1 in video : Range - 0 to 1023 */ + int view_id; /**< view identifier */ + unsigned char inter_view_flag; /**< 1 if used for inter-view prediction, 0 if not */ + unsigned char num_inter_view_refs_l0; /**< number of inter-view ref pics in RefPicList0 */ + unsigned char num_inter_view_refs_l1; /**< number of inter-view ref pics in RefPicList1 */ + unsigned char MVCReserved8Bits; /**< Reserved bits */ + int InterViewRefsL0[16]; /**< view id of the i-th view component for inter-view prediction in RefPicList0 */ + int InterViewRefsL1[16]; /**< view id of the i-th view component for inter-view prediction in RefPicList1 */ +} CUVIDH264MVCEXT; + +/*********************************************************/ +//! \struct CUVIDH264SVCEXT +//! H.264 SVC picture parameters ext +//! This structure is used in CUVIDH264PICPARAMS structure +/*********************************************************/ +typedef struct _CUVIDH264SVCEXT +{ + unsigned char profile_idc; + unsigned char level_idc; + unsigned char DQId; + unsigned char DQIdMax; + unsigned char disable_inter_layer_deblocking_filter_idc; + unsigned char ref_layer_chroma_phase_y_plus1; + signed char inter_layer_slice_alpha_c0_offset_div2; + signed char inter_layer_slice_beta_offset_div2; + + unsigned short DPBEntryValidFlag; + unsigned char inter_layer_deblocking_filter_control_present_flag; + unsigned char extended_spatial_scalability_idc; + unsigned char adaptive_tcoeff_level_prediction_flag; + unsigned char slice_header_restriction_flag; + unsigned char chroma_phase_x_plus1_flag; + unsigned char chroma_phase_y_plus1; + + unsigned char tcoeff_level_prediction_flag; + unsigned char constrained_intra_resampling_flag; + unsigned char ref_layer_chroma_phase_x_plus1_flag; + unsigned char store_ref_base_pic_flag; + unsigned char Reserved8BitsA; + unsigned char Reserved8BitsB; + + short scaled_ref_layer_left_offset; + short scaled_ref_layer_top_offset; + short scaled_ref_layer_right_offset; + short scaled_ref_layer_bottom_offset; + unsigned short Reserved16Bits; + struct _CUVIDPICPARAMS *pNextLayer; /**< Points to the picparams for the next layer to be decoded. + Linked list ends at the target layer. */ + int bRefBaseLayer; /**< whether to store ref base pic */ +} CUVIDH264SVCEXT; + +/******************************************************/ +//! \struct CUVIDH264PICPARAMS +//! H.264 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/******************************************************/ +typedef struct _CUVIDH264PICPARAMS +{ + // SPS + int log2_max_frame_num_minus4; + int pic_order_cnt_type; + int log2_max_pic_order_cnt_lsb_minus4; + int delta_pic_order_always_zero_flag; + int frame_mbs_only_flag; + int direct_8x8_inference_flag; + int num_ref_frames; // NOTE: shall meet level 4.1 restrictions + unsigned char residual_colour_transform_flag; + unsigned char bit_depth_luma_minus8; // Must be 0 (only 8-bit supported) + unsigned char bit_depth_chroma_minus8; // Must be 0 (only 8-bit supported) + unsigned char qpprime_y_zero_transform_bypass_flag; + // PPS + int entropy_coding_mode_flag; + int pic_order_present_flag; + int num_ref_idx_l0_active_minus1; + int num_ref_idx_l1_active_minus1; + int weighted_pred_flag; + int weighted_bipred_idc; + int pic_init_qp_minus26; + int deblocking_filter_control_present_flag; + int redundant_pic_cnt_present_flag; + int transform_8x8_mode_flag; + int MbaffFrameFlag; + int constrained_intra_pred_flag; + int chroma_qp_index_offset; + int second_chroma_qp_index_offset; + int ref_pic_flag; + int frame_num; + int CurrFieldOrderCnt[2]; + // DPB + CUVIDH264DPBENTRY dpb[16]; // List of reference frames within the DPB + // Quantization Matrices (raster-order) + unsigned char WeightScale4x4[6][16]; + unsigned char WeightScale8x8[2][64]; + // FMO/ASO + unsigned char fmo_aso_enable; + unsigned char num_slice_groups_minus1; + unsigned char slice_group_map_type; + signed char pic_init_qs_minus26; + unsigned int slice_group_change_rate_minus1; + union + { + unsigned long long slice_group_map_addr; + const unsigned char *pMb2SliceGroupMap; + } fmo; + unsigned int Reserved[12]; + // SVC/MVC + union + { + CUVIDH264MVCEXT mvcext; + CUVIDH264SVCEXT svcext; + }; +} CUVIDH264PICPARAMS; + + +/********************************************************/ +//! \struct CUVIDMPEG2PICPARAMS +//! MPEG-2 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/********************************************************/ +typedef struct _CUVIDMPEG2PICPARAMS +{ + int ForwardRefIdx; // Picture index of forward reference (P/B-frames) + int BackwardRefIdx; // Picture index of backward reference (B-frames) + int picture_coding_type; + int full_pel_forward_vector; + int full_pel_backward_vector; + int f_code[2][2]; + int intra_dc_precision; + int frame_pred_frame_dct; + int concealment_motion_vectors; + int q_scale_type; + int intra_vlc_format; + int alternate_scan; + int top_field_first; + // Quantization matrices (raster order) + unsigned char QuantMatrixIntra[64]; + unsigned char QuantMatrixInter[64]; +} CUVIDMPEG2PICPARAMS; + +// MPEG-4 has VOP types instead of Picture types +#define I_VOP 0 +#define P_VOP 1 +#define B_VOP 2 +#define S_VOP 3 + +/*******************************************************/ +//! \struct CUVIDMPEG4PICPARAMS +//! MPEG-4 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/*******************************************************/ +typedef struct _CUVIDMPEG4PICPARAMS +{ + int ForwardRefIdx; // Picture index of forward reference (P/B-frames) + int BackwardRefIdx; // Picture index of backward reference (B-frames) + // VOL + int video_object_layer_width; + int video_object_layer_height; + int vop_time_increment_bitcount; + int top_field_first; + int resync_marker_disable; + int quant_type; + int quarter_sample; + int short_video_header; + int divx_flags; + // VOP + int vop_coding_type; + int vop_coded; + int vop_rounding_type; + int alternate_vertical_scan_flag; + int interlaced; + int vop_fcode_forward; + int vop_fcode_backward; + int trd[2]; + int trb[2]; + // Quantization matrices (raster order) + unsigned char QuantMatrixIntra[64]; + unsigned char QuantMatrixInter[64]; + int gmc_enabled; +} CUVIDMPEG4PICPARAMS; + +/********************************************************/ +//! \struct CUVIDVC1PICPARAMS +//! VC1 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/********************************************************/ +typedef struct _CUVIDVC1PICPARAMS +{ + int ForwardRefIdx; /**< Picture index of forward reference (P/B-frames) */ + int BackwardRefIdx; /**< Picture index of backward reference (B-frames) */ + int FrameWidth; /**< Actual frame width */ + int FrameHeight; /**< Actual frame height */ + // PICTURE + int intra_pic_flag; /**< Set to 1 for I,BI frames */ + int ref_pic_flag; /**< Set to 1 for I,P frames */ + int progressive_fcm; /**< Progressive frame */ + // SEQUENCE + int profile; + int postprocflag; + int pulldown; + int interlace; + int tfcntrflag; + int finterpflag; + int psf; + int multires; + int syncmarker; + int rangered; + int maxbframes; + // ENTRYPOINT + int panscan_flag; + int refdist_flag; + int extended_mv; + int dquant; + int vstransform; + int loopfilter; + int fastuvmc; + int overlap; + int quantizer; + int extended_dmv; + int range_mapy_flag; + int range_mapy; + int range_mapuv_flag; + int range_mapuv; + int rangeredfrm; // range reduction state +} CUVIDVC1PICPARAMS; + +/***********************************************************/ +//! \struct CUVIDJPEGPICPARAMS +//! JPEG picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/***********************************************************/ +typedef struct _CUVIDJPEGPICPARAMS +{ + int Reserved; +} CUVIDJPEGPICPARAMS; + + +/*******************************************************/ +//! \struct CUVIDHEVCPICPARAMS +//! HEVC picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/*******************************************************/ +typedef struct _CUVIDHEVCPICPARAMS +{ + // sps + int pic_width_in_luma_samples; + int pic_height_in_luma_samples; + unsigned char log2_min_luma_coding_block_size_minus3; + unsigned char log2_diff_max_min_luma_coding_block_size; + unsigned char log2_min_transform_block_size_minus2; + unsigned char log2_diff_max_min_transform_block_size; + unsigned char pcm_enabled_flag; + unsigned char log2_min_pcm_luma_coding_block_size_minus3; + unsigned char log2_diff_max_min_pcm_luma_coding_block_size; + unsigned char pcm_sample_bit_depth_luma_minus1; + + unsigned char pcm_sample_bit_depth_chroma_minus1; + unsigned char pcm_loop_filter_disabled_flag; + unsigned char strong_intra_smoothing_enabled_flag; + unsigned char max_transform_hierarchy_depth_intra; + unsigned char max_transform_hierarchy_depth_inter; + unsigned char amp_enabled_flag; + unsigned char separate_colour_plane_flag; + unsigned char log2_max_pic_order_cnt_lsb_minus4; + + unsigned char num_short_term_ref_pic_sets; + unsigned char long_term_ref_pics_present_flag; + unsigned char num_long_term_ref_pics_sps; + unsigned char sps_temporal_mvp_enabled_flag; + unsigned char sample_adaptive_offset_enabled_flag; + unsigned char scaling_list_enable_flag; + unsigned char IrapPicFlag; + unsigned char IdrPicFlag; + + unsigned char bit_depth_luma_minus8; + unsigned char bit_depth_chroma_minus8; + //sps/pps extension fields + unsigned char log2_max_transform_skip_block_size_minus2; + unsigned char log2_sao_offset_scale_luma; + unsigned char log2_sao_offset_scale_chroma; + unsigned char high_precision_offsets_enabled_flag; + unsigned char reserved1[10]; + + // pps + unsigned char dependent_slice_segments_enabled_flag; + unsigned char slice_segment_header_extension_present_flag; + unsigned char sign_data_hiding_enabled_flag; + unsigned char cu_qp_delta_enabled_flag; + unsigned char diff_cu_qp_delta_depth; + signed char init_qp_minus26; + signed char pps_cb_qp_offset; + signed char pps_cr_qp_offset; + + unsigned char constrained_intra_pred_flag; + unsigned char weighted_pred_flag; + unsigned char weighted_bipred_flag; + unsigned char transform_skip_enabled_flag; + unsigned char transquant_bypass_enabled_flag; + unsigned char entropy_coding_sync_enabled_flag; + unsigned char log2_parallel_merge_level_minus2; + unsigned char num_extra_slice_header_bits; + + unsigned char loop_filter_across_tiles_enabled_flag; + unsigned char loop_filter_across_slices_enabled_flag; + unsigned char output_flag_present_flag; + unsigned char num_ref_idx_l0_default_active_minus1; + unsigned char num_ref_idx_l1_default_active_minus1; + unsigned char lists_modification_present_flag; + unsigned char cabac_init_present_flag; + unsigned char pps_slice_chroma_qp_offsets_present_flag; + + unsigned char deblocking_filter_override_enabled_flag; + unsigned char pps_deblocking_filter_disabled_flag; + signed char pps_beta_offset_div2; + signed char pps_tc_offset_div2; + unsigned char tiles_enabled_flag; + unsigned char uniform_spacing_flag; + unsigned char num_tile_columns_minus1; + unsigned char num_tile_rows_minus1; + + unsigned short column_width_minus1[21]; + unsigned short row_height_minus1[21]; + + // sps and pps extension HEVC-main 444 + unsigned char sps_range_extension_flag; + unsigned char transform_skip_rotation_enabled_flag; + unsigned char transform_skip_context_enabled_flag; + unsigned char implicit_rdpcm_enabled_flag; + + unsigned char explicit_rdpcm_enabled_flag; + unsigned char extended_precision_processing_flag; + unsigned char intra_smoothing_disabled_flag; + unsigned char persistent_rice_adaptation_enabled_flag; + + unsigned char cabac_bypass_alignment_enabled_flag; + unsigned char pps_range_extension_flag; + unsigned char cross_component_prediction_enabled_flag; + unsigned char chroma_qp_offset_list_enabled_flag; + + unsigned char diff_cu_chroma_qp_offset_depth; + unsigned char chroma_qp_offset_list_len_minus1; + signed char cb_qp_offset_list[6]; + + signed char cr_qp_offset_list[6]; + unsigned char reserved2[2]; + + unsigned int reserved3[8]; + + // RefPicSets + int NumBitsForShortTermRPSInSlice; + int NumDeltaPocsOfRefRpsIdx; + int NumPocTotalCurr; + int NumPocStCurrBefore; + int NumPocStCurrAfter; + int NumPocLtCurr; + int CurrPicOrderCntVal; + int RefPicIdx[16]; // [refpic] Indices of valid reference pictures (-1 if unused for reference) + int PicOrderCntVal[16]; // [refpic] + unsigned char IsLongTerm[16]; // [refpic] 0=not a long-term reference, 1=long-term reference + unsigned char RefPicSetStCurrBefore[8]; // [0..NumPocStCurrBefore-1] -> refpic (0..15) + unsigned char RefPicSetStCurrAfter[8]; // [0..NumPocStCurrAfter-1] -> refpic (0..15) + unsigned char RefPicSetLtCurr[8]; // [0..NumPocLtCurr-1] -> refpic (0..15) + unsigned char RefPicSetInterLayer0[8]; + unsigned char RefPicSetInterLayer1[8]; + unsigned int reserved4[12]; + + // scaling lists (diag order) + unsigned char ScalingList4x4[6][16]; // [matrixId][i] + unsigned char ScalingList8x8[6][64]; // [matrixId][i] + unsigned char ScalingList16x16[6][64]; // [matrixId][i] + unsigned char ScalingList32x32[2][64]; // [matrixId][i] + unsigned char ScalingListDCCoeff16x16[6]; // [matrixId] + unsigned char ScalingListDCCoeff32x32[2]; // [matrixId] +} CUVIDHEVCPICPARAMS; + + +/***********************************************************/ +//! \struct CUVIDVP8PICPARAMS +//! VP8 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/***********************************************************/ +typedef struct _CUVIDVP8PICPARAMS +{ + int width; + int height; + unsigned int first_partition_size; + //Frame Indexes + unsigned char LastRefIdx; + unsigned char GoldenRefIdx; + unsigned char AltRefIdx; + union { + struct { + unsigned char frame_type : 1; /**< 0 = KEYFRAME, 1 = INTERFRAME */ + unsigned char version : 3; + unsigned char show_frame : 1; + unsigned char update_mb_segmentation_data : 1; /**< Must be 0 if segmentation is not enabled */ + unsigned char Reserved2Bits : 2; + }vp8_frame_tag; + unsigned char wFrameTagFlags; + }; + unsigned char Reserved1[4]; + unsigned int Reserved2[3]; +} CUVIDVP8PICPARAMS; + +/***********************************************************/ +//! \struct CUVIDVP9PICPARAMS +//! VP9 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/***********************************************************/ +typedef struct _CUVIDVP9PICPARAMS +{ + unsigned int width; + unsigned int height; + + //Frame Indices + unsigned char LastRefIdx; + unsigned char GoldenRefIdx; + unsigned char AltRefIdx; + unsigned char colorSpace; + + unsigned short profile : 3; + unsigned short frameContextIdx : 2; + unsigned short frameType : 1; + unsigned short showFrame : 1; + unsigned short errorResilient : 1; + unsigned short frameParallelDecoding : 1; + unsigned short subSamplingX : 1; + unsigned short subSamplingY : 1; + unsigned short intraOnly : 1; + unsigned short allow_high_precision_mv : 1; + unsigned short refreshEntropyProbs : 1; + unsigned short reserved2Bits : 2; + + unsigned short reserved16Bits; + + unsigned char refFrameSignBias[4]; + + unsigned char bitDepthMinus8Luma; + unsigned char bitDepthMinus8Chroma; + unsigned char loopFilterLevel; + unsigned char loopFilterSharpness; + + unsigned char modeRefLfEnabled; + unsigned char log2_tile_columns; + unsigned char log2_tile_rows; + + unsigned char segmentEnabled : 1; + unsigned char segmentMapUpdate : 1; + unsigned char segmentMapTemporalUpdate : 1; + unsigned char segmentFeatureMode : 1; + unsigned char reserved4Bits : 4; + + + unsigned char segmentFeatureEnable[8][4]; + short segmentFeatureData[8][4]; + unsigned char mb_segment_tree_probs[7]; + unsigned char segment_pred_probs[3]; + unsigned char reservedSegment16Bits[2]; + + int qpYAc; + int qpYDc; + int qpChDc; + int qpChAc; + + unsigned int activeRefIdx[3]; + unsigned int resetFrameContext; + unsigned int mcomp_filter_type; + unsigned int mbRefLfDelta[4]; + unsigned int mbModeLfDelta[2]; + unsigned int frameTagSize; + unsigned int offsetToDctParts; + unsigned int reserved128Bits[4]; + +} CUVIDVP9PICPARAMS; + +/***********************************************************/ +//! \struct CUVIDAV1PICPARAMS +//! AV1 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/***********************************************************/ +typedef struct _CUVIDAV1PICPARAMS +{ + unsigned int width; // coded width, if superres enabled then it is upscaled width + unsigned int height; // coded height + unsigned int frame_offset; // defined as order_hint in AV1 specification + int decodePicIdx; // decoded output pic index, if film grain enabled, it will keep decoded (without film grain) output + // It can be used as reference frame for future frames + + // sequence header + unsigned int profile : 3; // 0 = profile0, 1 = profile1, 2 = profile2 + unsigned int use_128x128_superblock : 1; // superblock size 0:64x64, 1: 128x128 + unsigned int subsampling_x : 1; // (subsampling_x, _y) 1,1 = 420, 1,0 = 422, 0,0 = 444 + unsigned int subsampling_y : 1; + unsigned int mono_chrome : 1; // for monochrome content, mono_chrome = 1 and (subsampling_x, _y) should be 1,1 + unsigned int bit_depth_minus8 : 4; // bit depth minus 8 + unsigned int enable_filter_intra : 1; // tool enable in seq level, 0 : disable 1: frame header control + unsigned int enable_intra_edge_filter : 1; // intra edge filtering process, 0 : disable 1: enabled + unsigned int enable_interintra_compound : 1; // interintra, 0 : not present 1: present + unsigned int enable_masked_compound : 1; // 1: mode info for inter blocks may contain the syntax element compound_type. + // 0: syntax element compound_type will not be present + unsigned int enable_dual_filter : 1; // vertical and horiz filter selection, 1: enable and 0: disable + unsigned int enable_order_hint : 1; // order hint, and related tools, 1: enable and 0: disable + unsigned int order_hint_bits_minus1 : 3; // is used to compute OrderHintBits + unsigned int enable_jnt_comp : 1; // joint compound modes, 1: enable and 0: disable + unsigned int enable_superres : 1; // superres in seq level, 0 : disable 1: frame level control + unsigned int enable_cdef : 1; // cdef filtering in seq level, 0 : disable 1: frame level control + unsigned int enable_restoration : 1; // loop restoration filtering in seq level, 0 : disable 1: frame level control + unsigned int enable_fgs : 1; // defined as film_grain_params_present in AV1 specification + unsigned int reserved0_7bits : 7; // reserved bits; must be set to 0 + + // frame header + unsigned int frame_type : 2 ; // 0:Key frame, 1:Inter frame, 2:intra only, 3:s-frame + unsigned int show_frame : 1 ; // show_frame = 1 implies that frame should be immediately output once decoded + unsigned int disable_cdf_update : 1; // CDF update during symbol decoding, 1: disabled, 0: enabled + unsigned int allow_screen_content_tools : 1; // 1: intra blocks may use palette encoding, 0: palette encoding is never used + unsigned int force_integer_mv : 1; // 1: motion vectors will always be integers, 0: can contain fractional bits + unsigned int coded_denom : 3; // coded_denom of the superres scale as specified in AV1 specification + unsigned int allow_intrabc : 1; // 1: intra block copy may be used, 0: intra block copy is not allowed + unsigned int allow_high_precision_mv : 1; // 1/8 precision mv enable + unsigned int interp_filter : 3; // interpolation filter. Refer to section 6.8.9 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned int switchable_motion_mode : 1; // defined as is_motion_mode_switchable in AV1 specification + unsigned int use_ref_frame_mvs : 1; // 1: current frame can use the previous frame mv information, 0: will not use. + unsigned int disable_frame_end_update_cdf : 1; // 1: indicates that the end of frame CDF update is disabled + unsigned int delta_q_present : 1; // quantizer index delta values are present in the block level + unsigned int delta_q_res : 2; // left shift which should be applied to decoded quantizer index delta values + unsigned int using_qmatrix : 1; // 1: quantizer matrix will be used to compute quantizers + unsigned int coded_lossless : 1; // 1: all segments use lossless coding + unsigned int use_superres : 1; // 1: superres enabled for frame + unsigned int tx_mode : 2; // 0: ONLY4x4,1:LARGEST,2:SELECT + unsigned int reference_mode : 1; // 0: SINGLE, 1: SELECT + unsigned int allow_warped_motion : 1; // 1: allow_warped_motion may be present, 0: allow_warped_motion will not be present + unsigned int reduced_tx_set : 1; // 1: frame is restricted to subset of the full set of transform types, 0: no such restriction + unsigned int skip_mode : 1; // 1: most of the mode info is skipped, 0: mode info is not skipped + unsigned int reserved1_3bits : 3; // reserved bits; must be set to 0 + + // tiling info + unsigned int num_tile_cols : 8; // number of tiles across the frame., max is 64 + unsigned int num_tile_rows : 8; // number of tiles down the frame., max is 64 + unsigned int context_update_tile_id : 16; // specifies which tile to use for the CDF update + unsigned short tile_widths[64]; // Width of each column in superblocks + unsigned short tile_heights[64]; // height of each row in superblocks + + // CDEF - refer to section 6.10.14 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned char cdef_damping_minus_3 : 2; // controls the amount of damping in the deringing filter + unsigned char cdef_bits : 2; // the number of bits needed to specify which CDEF filter to apply + unsigned char reserved2_4bits : 4; // reserved bits; must be set to 0 + unsigned char cdef_y_strength[8]; // 0-3 bits: y_pri_strength, 4-7 bits y_sec_strength + unsigned char cdef_uv_strength[8]; // 0-3 bits: uv_pri_strength, 4-7 bits uv_sec_strength + + // SkipModeFrames + unsigned char SkipModeFrame0 : 4; // specifies the frames to use for compound prediction when skip_mode is equal to 1. + unsigned char SkipModeFrame1 : 4; + + // qp information - refer to section 6.8.11 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned char base_qindex; // indicates the base frame qindex. Defined as base_q_idx in AV1 specification + char qp_y_dc_delta_q; // indicates the Y DC quantizer relative to base_q_idx. Defined as DeltaQYDc in AV1 specification + char qp_u_dc_delta_q; // indicates the U DC quantizer relative to base_q_idx. Defined as DeltaQUDc in AV1 specification + char qp_v_dc_delta_q; // indicates the V DC quantizer relative to base_q_idx. Defined as DeltaQVDc in AV1 specification + char qp_u_ac_delta_q; // indicates the U AC quantizer relative to base_q_idx. Defined as DeltaQUAc in AV1 specification + char qp_v_ac_delta_q; // indicates the V AC quantizer relative to base_q_idx. Defined as DeltaQVAc in AV1 specification + unsigned char qm_y; // specifies the level in the quantizer matrix that should be used for luma plane decoding + unsigned char qm_u; // specifies the level in the quantizer matrix that should be used for chroma U plane decoding + unsigned char qm_v; // specifies the level in the quantizer matrix that should be used for chroma V plane decoding + + // segmentation - refer to section 6.8.13 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned char segmentation_enabled : 1; // 1 indicates that this frame makes use of the segmentation tool + unsigned char segmentation_update_map : 1; // 1 indicates that the segmentation map are updated during the decoding of this frame + unsigned char segmentation_update_data : 1; // 1 indicates that new parameters are about to be specified for each segment + unsigned char segmentation_temporal_update : 1; // 1 indicates that the updates to the segmentation map are coded relative to the existing segmentation map + unsigned char reserved3_4bits : 4; // reserved bits; must be set to 0 + short segmentation_feature_data[8][8]; // specifies the feature data for a segment feature + unsigned char segmentation_feature_mask[8]; // indicates that the corresponding feature is unused or feature value is coded + + // loopfilter - refer to section 6.8.10 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned char loop_filter_level[2]; // contains loop filter strength values + unsigned char loop_filter_level_u; // loop filter strength value of U plane + unsigned char loop_filter_level_v; // loop filter strength value of V plane + unsigned char loop_filter_sharpness; // indicates the sharpness level + char loop_filter_ref_deltas[8]; // contains the adjustment needed for the filter level based on the chosen reference frame + char loop_filter_mode_deltas[2]; // contains the adjustment needed for the filter level based on the chosen mode + unsigned char loop_filter_delta_enabled : 1; // indicates that the filter level depends on the mode and reference frame used to predict a block + unsigned char loop_filter_delta_update : 1; // indicates that additional syntax elements are present that specify which mode and + // reference frame deltas are to be updated + unsigned char delta_lf_present : 1; // specifies whether loop filter delta values are present in the block level + unsigned char delta_lf_res : 2; // specifies the left shift to apply to the decoded loop filter values + unsigned char delta_lf_multi : 1; // separate loop filter deltas for Hy,Vy,U,V edges + unsigned char reserved4_2bits : 2; // reserved bits; must be set to 0 + + // restoration - refer to section 6.10.15 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned char lr_unit_size[3]; // specifies the size of loop restoration units: 0: 32, 1: 64, 2: 128, 3: 256 + unsigned char lr_type[3] ; // used to compute FrameRestorationType + + // reference frames + unsigned char primary_ref_frame; // specifies which reference frame contains the CDF values and other state that should be + // loaded at the start of the frame + unsigned char ref_frame_map[8]; // frames in dpb that can be used as reference for current or future frames + + unsigned char temporal_layer_id : 4; // temporal layer id + unsigned char spatial_layer_id : 4; // spatial layer id + + unsigned char reserved5_32bits[4]; // reserved bits; must be set to 0 + + // ref frame list + struct + { + unsigned int width; + unsigned int height; + unsigned char index; + unsigned char reserved24Bits[3]; // reserved bits; must be set to 0 + } ref_frame[7]; // frames used as reference frame for current frame. + + // global motion + struct { + unsigned char invalid : 1; + unsigned char wmtype : 2; // defined as GmType in AV1 specification + unsigned char reserved5Bits : 5; // reserved bits; must be set to 0 + char reserved24Bits[3]; // reserved bits; must be set to 0 + int wmmat[6]; // defined as gm_params[] in AV1 specification + } global_motion[7]; // global motion params for reference frames + + // film grain params - refer to section 6.8.20 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned short apply_grain : 1; + unsigned short overlap_flag : 1; + unsigned short scaling_shift_minus8 : 2; + unsigned short chroma_scaling_from_luma : 1; + unsigned short ar_coeff_lag : 2; + unsigned short ar_coeff_shift_minus6 : 2; + unsigned short grain_scale_shift : 2; + unsigned short clip_to_restricted_range : 1; + unsigned short reserved6_4bits : 4; // reserved bits; must be set to 0 + unsigned char num_y_points; + unsigned char scaling_points_y[14][2]; + unsigned char num_cb_points; + unsigned char scaling_points_cb[10][2]; + unsigned char num_cr_points; + unsigned char scaling_points_cr[10][2]; + unsigned char reserved7_8bits; // reserved bits; must be set to 0 + unsigned short random_seed; + short ar_coeffs_y[24]; + short ar_coeffs_cb[25]; + short ar_coeffs_cr[25]; + unsigned char cb_mult; + unsigned char cb_luma_mult; + short cb_offset; + unsigned char cr_mult; + unsigned char cr_luma_mult; + short cr_offset; + + int reserved[7]; // reserved bits; must be set to 0 +} CUVIDAV1PICPARAMS; + +/******************************************************************************************/ +//! \struct CUVIDPICPARAMS +//! Picture parameters for decoding +//! This structure is used in cuvidDecodePicture API +//! IN for cuvidDecodePicture +/******************************************************************************************/ +typedef struct _CUVIDPICPARAMS +{ + int PicWidthInMbs; /**< IN: Coded frame size in macroblocks */ + int FrameHeightInMbs; /**< IN: Coded frame height in macroblocks */ + int CurrPicIdx; /**< IN: Output index of the current picture */ + int field_pic_flag; /**< IN: 0=frame picture, 1=field picture */ + int bottom_field_flag; /**< IN: 0=top field, 1=bottom field (ignored if field_pic_flag=0) */ + int second_field; /**< IN: Second field of a complementary field pair */ + // Bitstream data + unsigned int nBitstreamDataLen; /**< IN: Number of bytes in bitstream data buffer */ + const unsigned char *pBitstreamData; /**< IN: Ptr to bitstream data for this picture (slice-layer) */ + unsigned int nNumSlices; /**< IN: Number of slices in this picture */ + const unsigned int *pSliceDataOffsets; /**< IN: nNumSlices entries, contains offset of each slice within + the bitstream data buffer */ + int ref_pic_flag; /**< IN: This picture is a reference picture */ + int intra_pic_flag; /**< IN: This picture is entirely intra coded */ + unsigned int Reserved[30]; /**< Reserved for future use */ + // IN: Codec-specific data + union { + CUVIDMPEG2PICPARAMS mpeg2; /**< Also used for MPEG-1 */ + CUVIDH264PICPARAMS h264; + CUVIDVC1PICPARAMS vc1; + CUVIDMPEG4PICPARAMS mpeg4; + CUVIDJPEGPICPARAMS jpeg; + CUVIDHEVCPICPARAMS hevc; + CUVIDVP8PICPARAMS vp8; + CUVIDVP9PICPARAMS vp9; + CUVIDAV1PICPARAMS av1; + unsigned int CodecReserved[1024]; + } CodecSpecific; +} CUVIDPICPARAMS; + + +/******************************************************/ +//! \struct CUVIDPROCPARAMS +//! Picture parameters for postprocessing +//! This structure is used in cuvidMapVideoFrame API +/******************************************************/ +typedef struct _CUVIDPROCPARAMS +{ + int progressive_frame; /**< IN: Input is progressive (deinterlace_mode will be ignored) */ + int second_field; /**< IN: Output the second field (ignored if deinterlace mode is Weave) */ + int top_field_first; /**< IN: Input frame is top field first (1st field is top, 2nd field is bottom) */ + int unpaired_field; /**< IN: Input only contains one field (2nd field is invalid) */ + // The fields below are used for raw YUV input + unsigned int reserved_flags; /**< Reserved for future use (set to zero) */ + unsigned int reserved_zero; /**< Reserved (set to zero) */ + unsigned long long raw_input_dptr; /**< IN: Input CUdeviceptr for raw YUV extensions */ + unsigned int raw_input_pitch; /**< IN: pitch in bytes of raw YUV input (should be aligned appropriately) */ + unsigned int raw_input_format; /**< IN: Input YUV format (cudaVideoCodec_enum) */ + unsigned long long raw_output_dptr; /**< IN: Output CUdeviceptr for raw YUV extensions */ + unsigned int raw_output_pitch; /**< IN: pitch in bytes of raw YUV output (should be aligned appropriately) */ + unsigned int Reserved1; /**< Reserved for future use (set to zero) */ + CUstream output_stream; /**< IN: stream object used by cuvidMapVideoFrame */ + unsigned int Reserved[46]; /**< Reserved for future use (set to zero) */ + unsigned long long *histogram_dptr; /**< OUT: Output CUdeviceptr for histogram extensions */ + void *Reserved2[1]; /**< Reserved for future use (set to zero) */ +} CUVIDPROCPARAMS; + +/*********************************************************************************************************/ +//! \struct CUVIDGETDECODESTATUS +//! Struct for reporting decode status. +//! This structure is used in cuvidGetDecodeStatus API. +/*********************************************************************************************************/ +typedef struct _CUVIDGETDECODESTATUS +{ + cuvidDecodeStatus decodeStatus; + unsigned int reserved[31]; + void *pReserved[8]; +} CUVIDGETDECODESTATUS; + +/****************************************************/ +//! \struct CUVIDRECONFIGUREDECODERINFO +//! Struct for decoder reset +//! This structure is used in cuvidReconfigureDecoder() API +/****************************************************/ +typedef struct _CUVIDRECONFIGUREDECODERINFO +{ + unsigned int ulWidth; /**< IN: Coded sequence width in pixels, MUST be < = ulMaxWidth defined at CUVIDDECODECREATEINFO */ + unsigned int ulHeight; /**< IN: Coded sequence height in pixels, MUST be < = ulMaxHeight defined at CUVIDDECODECREATEINFO */ + unsigned int ulTargetWidth; /**< IN: Post processed output width */ + unsigned int ulTargetHeight; /**< IN: Post Processed output height */ + unsigned int ulNumDecodeSurfaces; /**< IN: Maximum number of internal decode surfaces */ + unsigned int reserved1[12]; /**< Reserved for future use. Set to Zero */ + /** + * IN: Area of frame to be displayed. Use-case : Source Cropping + */ + struct { + short left; + short top; + short right; + short bottom; + } display_area; + /** + * IN: Target Rectangle in the OutputFrame. Use-case : Aspect ratio Conversion + */ + struct { + short left; + short top; + short right; + short bottom; + } target_rect; + unsigned int reserved2[11]; /**< Reserved for future use. Set to Zero */ +} CUVIDRECONFIGUREDECODERINFO; + + +/***********************************************************************************************************/ +//! VIDEO_DECODER +//! +//! In order to minimize decode latencies, there should be always at least 2 pictures in the decode +//! queue at any time, in order to make sure that all decode engines are always busy. +//! +//! Overall data flow: +//! - cuvidGetDecoderCaps(...) +//! - cuvidCreateDecoder(...) +//! - For each picture: +//! + cuvidDecodePicture(N) +//! + cuvidMapVideoFrame(N-4) +//! + do some processing in cuda +//! + cuvidUnmapVideoFrame(N-4) +//! + cuvidDecodePicture(N+1) +//! + cuvidMapVideoFrame(N-3) +//! + ... +//! - cuvidDestroyDecoder(...) +//! +//! NOTE: +//! - When the cuda context is created from a D3D device, the D3D device must also be created +//! with the D3DCREATE_MULTITHREADED flag. +//! - There is a limit to how many pictures can be mapped simultaneously (ulNumOutputSurfaces) +//! - cuvidDecodePicture may block the calling thread if there are too many pictures pending +//! in the decode queue +/***********************************************************************************************************/ + + +/**********************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidGetDecoderCaps(CUVIDDECODECAPS *pdc) +//! Queries decode capabilities of NVDEC-HW based on CodecType, ChromaFormat and BitDepthMinus8 parameters. +//! 1. Application fills IN parameters CodecType, ChromaFormat and BitDepthMinus8 of CUVIDDECODECAPS structure +//! 2. On calling cuvidGetDecoderCaps, driver fills OUT parameters if the IN parameters are supported +//! If IN parameters passed to the driver are not supported by NVDEC-HW, then all OUT params are set to 0. +//! E.g. on Geforce GTX 960: +//! App fills - eCodecType = cudaVideoCodec_H264; eChromaFormat = cudaVideoChromaFormat_420; nBitDepthMinus8 = 0; +//! Given IN parameters are supported, hence driver fills: bIsSupported = 1; nMinWidth = 48; nMinHeight = 16; +//! nMaxWidth = 4096; nMaxHeight = 4096; nMaxMBCount = 65536; +//! CodedWidth*CodedHeight/256 must be less than or equal to nMaxMBCount +/**********************************************************************************************************************/ +extern CUresult CUDAAPI cuvidGetDecoderCaps(CUVIDDECODECAPS *pdc); + +/*****************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidCreateDecoder(CUvideodecoder *phDecoder, CUVIDDECODECREATEINFO *pdci) +//! Create the decoder object based on pdci. A handle to the created decoder is returned +/*****************************************************************************************************/ +extern CUresult CUDAAPI cuvidCreateDecoder(CUvideodecoder *phDecoder, CUVIDDECODECREATEINFO *pdci); + +/*****************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidDestroyDecoder(CUvideodecoder hDecoder) +//! Destroy the decoder object +/*****************************************************************************************************/ +extern CUresult CUDAAPI cuvidDestroyDecoder(CUvideodecoder hDecoder); + +/*****************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidDecodePicture(CUvideodecoder hDecoder, CUVIDPICPARAMS *pPicParams) +//! Decode a single picture (field or frame) +//! Kicks off HW decoding +/*****************************************************************************************************/ +extern CUresult CUDAAPI cuvidDecodePicture(CUvideodecoder hDecoder, CUVIDPICPARAMS *pPicParams); + +/************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidGetDecodeStatus(CUvideodecoder hDecoder, int nPicIdx); +//! Get the decode status for frame corresponding to nPicIdx +//! API is supported for Maxwell and above generation GPUs. +//! API is currently supported for HEVC, H264 and JPEG codecs. +//! API returns CUDA_ERROR_NOT_SUPPORTED error code for unsupported GPU or codec. +/************************************************************************************************************/ +extern CUresult CUDAAPI cuvidGetDecodeStatus(CUvideodecoder hDecoder, int nPicIdx, CUVIDGETDECODESTATUS* pDecodeStatus); + +/*********************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidReconfigureDecoder(CUvideodecoder hDecoder, CUVIDRECONFIGUREDECODERINFO *pDecReconfigParams) +//! Used to reuse single decoder for multiple clips. Currently supports resolution change, resize params, display area +//! params, target area params change for same codec. Must be called during CUVIDPARSERPARAMS::pfnSequenceCallback +/*********************************************************************************************************/ +extern CUresult CUDAAPI cuvidReconfigureDecoder(CUvideodecoder hDecoder, CUVIDRECONFIGUREDECODERINFO *pDecReconfigParams); + + +#if !defined(__CUVID_DEVPTR64) || defined(__CUVID_INTERNAL) +/************************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidMapVideoFrame(CUvideodecoder hDecoder, int nPicIdx, unsigned int *pDevPtr, +//! unsigned int *pPitch, CUVIDPROCPARAMS *pVPP); +//! Post-process and map video frame corresponding to nPicIdx for use in cuda. Returns cuda device pointer and associated +//! pitch of the video frame +/************************************************************************************************************************/ +extern CUresult CUDAAPI cuvidMapVideoFrame(CUvideodecoder hDecoder, int nPicIdx, + unsigned int *pDevPtr, unsigned int *pPitch, + CUVIDPROCPARAMS *pVPP); + +/*****************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidUnmapVideoFrame(CUvideodecoder hDecoder, unsigned int DevPtr) +//! Unmap a previously mapped video frame +/*****************************************************************************************************/ +extern CUresult CUDAAPI cuvidUnmapVideoFrame(CUvideodecoder hDecoder, unsigned int DevPtr); +#endif + +#if defined(_WIN64) || defined(__LP64__) || defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) +/****************************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidMapVideoFrame64(CUvideodecoder hDecoder, int nPicIdx, unsigned long long *pDevPtr, +//! unsigned int * pPitch, CUVIDPROCPARAMS *pVPP); +//! Post-process and map video frame corresponding to nPicIdx for use in cuda. Returns cuda device pointer and associated +//! pitch of the video frame +/****************************************************************************************************************************/ +extern CUresult CUDAAPI cuvidMapVideoFrame64(CUvideodecoder hDecoder, int nPicIdx, unsigned long long *pDevPtr, + unsigned int *pPitch, CUVIDPROCPARAMS *pVPP); + +/**************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidUnmapVideoFrame64(CUvideodecoder hDecoder, unsigned long long DevPtr); +//! Unmap a previously mapped video frame +/**************************************************************************************************/ +extern CUresult CUDAAPI cuvidUnmapVideoFrame64(CUvideodecoder hDecoder, unsigned long long DevPtr); + +#if defined(__CUVID_DEVPTR64) && !defined(__CUVID_INTERNAL) +#define cuvidMapVideoFrame cuvidMapVideoFrame64 +#define cuvidUnmapVideoFrame cuvidUnmapVideoFrame64 +#endif +#endif + + + +/********************************************************************************************************************/ +//! +//! Context-locking: to facilitate multi-threaded implementations, the following 4 functions +//! provide a simple mutex-style host synchronization. If a non-NULL context is specified +//! in CUVIDDECODECREATEINFO, the codec library will acquire the mutex associated with the given +//! context before making any cuda calls. +//! A multi-threaded application could create a lock associated with a context handle so that +//! multiple threads can safely share the same cuda context: +//! - use cuCtxPopCurrent immediately after context creation in order to create a 'floating' context +//! that can be passed to cuvidCtxLockCreate. +//! - When using a floating context, all cuda calls should only be made within a cuvidCtxLock/cuvidCtxUnlock section. +//! +//! NOTE: This is a safer alternative to cuCtxPushCurrent and cuCtxPopCurrent, and is not related to video +//! decoder in any way (implemented as a critical section associated with cuCtx{Push|Pop}Current calls). +/********************************************************************************************************************/ + +/********************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidCtxLockCreate(CUvideoctxlock *pLock, CUcontext ctx) +//! This API is used to create CtxLock object +/********************************************************************************************************************/ +extern CUresult CUDAAPI cuvidCtxLockCreate(CUvideoctxlock *pLock, CUcontext ctx); + +/********************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidCtxLockDestroy(CUvideoctxlock lck) +//! This API is used to free CtxLock object +/********************************************************************************************************************/ +extern CUresult CUDAAPI cuvidCtxLockDestroy(CUvideoctxlock lck); + +/********************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidCtxLock(CUvideoctxlock lck, unsigned int reserved_flags) +//! This API is used to acquire ctxlock +/********************************************************************************************************************/ +extern CUresult CUDAAPI cuvidCtxLock(CUvideoctxlock lck, unsigned int reserved_flags); + +/********************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidCtxUnlock(CUvideoctxlock lck, unsigned int reserved_flags) +//! This API is used to release ctxlock +/********************************************************************************************************************/ +extern CUresult CUDAAPI cuvidCtxUnlock(CUvideoctxlock lck, unsigned int reserved_flags); + +/**********************************************************************************************/ + + +#if defined(__cplusplus) +} +// Auto-lock helper for C++ applications +class CCtxAutoLock +{ +private: + CUvideoctxlock m_ctx; +public: + CCtxAutoLock(CUvideoctxlock ctx):m_ctx(ctx) { cuvidCtxLock(m_ctx,0); } + ~CCtxAutoLock() { cuvidCtxUnlock(m_ctx,0); } +}; +#endif /* __cplusplus */ + +#endif // __CUDA_VIDEO_H__ + diff --git a/thirdparty/nvcodec/Interface/nvEncodeAPI.h b/thirdparty/nvcodec/Interface/nvEncodeAPI.h new file mode 100644 index 0000000..12e6c5b --- /dev/null +++ b/thirdparty/nvcodec/Interface/nvEncodeAPI.h @@ -0,0 +1,5003 @@ +/* + * This copyright notice applies to this header file only: + * + * Copyright (c) 2010-2020 NVIDIA Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the software, and to permit persons to whom the + * software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * \file nvEncodeAPI.h + * NVIDIA GPUs - beginning with the Kepler generation - contain a + * hardware-based encoder (referred to as NVENC) which provides + * fully-accelerated hardware-based video encoding. NvEncodeAPI provides the + * interface for NVIDIA video encoder (NVENC). \date 2011-2020 This file + * contains the interface constants, structure definitions and function + * prototypes. + */ + +#ifndef _NV_ENCODEAPI_H_ +#define _NV_ENCODEAPI_H_ + +#include + +#ifdef _WIN32 +#include +#endif + +#ifdef _MSC_VER +#ifndef _STDINT +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +#endif +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup ENCODER_STRUCTURE NvEncodeAPI Data structures + * @{ + */ + +#ifdef _WIN32 +#define NVENCAPI __stdcall +typedef RECT NVENC_RECT; +#else +#define NVENCAPI +// ========================================================================================= +#if !defined(GUID) && !defined(GUID_DEFINED) +/*! + * \struct GUID + * Abstracts the GUID structure for non-windows platforms. + */ +// ========================================================================================= +typedef struct { + uint32_t + Data1; /**< [in]: Specifies the first 8 hexadecimal digits of the GUID. */ + uint16_t + Data2; /**< [in]: Specifies the first group of 4 hexadecimal digits. */ + uint16_t + Data3; /**< [in]: Specifies the second group of 4 hexadecimal digits. */ + uint8_t Data4[8]; /**< [in]: Array of 8 bytes. The first 2 bytes contain the + third group of 4 hexadecimal digits. The remaining 6 + bytes contain the final 12 hexadecimal digits. */ +} GUID; +#endif // GUID + +/** + * \struct _NVENC_RECT + * Defines a Rectangle. Used in ::NV_ENC_PREPROCESS_FRAME. + */ +typedef struct _NVENC_RECT { + uint32_t left; /**< [in]: X coordinate of the upper left corner of rectangular + area to be specified. */ + uint32_t top; /**< [in]: Y coordinate of the upper left corner of the + rectangular area to be specified. */ + uint32_t right; /**< [in]: X coordinate of the bottom right corner of the + rectangular area to be specified. */ + uint32_t bottom; /**< [in]: Y coordinate of the bottom right corner of the + rectangular area to be specified. */ +} NVENC_RECT; + +#endif // _WIN32 + +/** @} */ /* End of GUID and NVENC_RECT structure grouping*/ + +typedef void* NV_ENC_INPUT_PTR; /**< NVENCODE API input buffer */ +typedef void* NV_ENC_OUTPUT_PTR; /**< NVENCODE API output buffer*/ +typedef void* NV_ENC_REGISTERED_PTR; /**< A Resource that has been registered + with NVENCODE API*/ +typedef void* NV_ENC_CUSTREAM_PTR; /**< Pointer to CUstream*/ + +#define NVENCAPI_MAJOR_VERSION 11 +#define NVENCAPI_MINOR_VERSION 0 + +#define NVENCAPI_VERSION \ + (NVENCAPI_MAJOR_VERSION | (NVENCAPI_MINOR_VERSION << 24)) + +/** + * Macro to generate per-structure version for use with API. + */ +#define NVENCAPI_STRUCT_VERSION(ver) \ + ((uint32_t)NVENCAPI_VERSION | ((ver) << 16) | (0x7 << 28)) + +#define NVENC_INFINITE_GOPLENGTH 0xffffffff + +#define NV_MAX_SEQ_HDR_LEN (512) + +#ifdef __GNUC__ +#define NV_ENC_DEPRECATED \ + __attribute__(( \ + deprecated("WILL BE REMOVED IN A FUTURE VIDEO CODEC SDK VERSION"))) +#elif defined(_MSC_VER) +#define NV_ENC_DEPRECATED \ + __declspec( \ + deprecated("WILL BE REMOVED IN A FUTURE VIDEO CODEC SDK " \ + "VERSION")) +#endif + +// ========================================================================================= +// Encode Codec GUIDS supported by the NvEncodeAPI interface. +// ========================================================================================= + +// {6BC82762-4E63-4ca4-AA85-1E50F321F6BF} +static const GUID NV_ENC_CODEC_H264_GUID = { + 0x6bc82762, + 0x4e63, + 0x4ca4, + {0xaa, 0x85, 0x1e, 0x50, 0xf3, 0x21, 0xf6, 0xbf}}; + +// {790CDC88-4522-4d7b-9425-BDA9975F7603} +static const GUID NV_ENC_CODEC_HEVC_GUID = { + 0x790cdc88, + 0x4522, + 0x4d7b, + {0x94, 0x25, 0xbd, 0xa9, 0x97, 0x5f, 0x76, 0x3}}; + +// ========================================================================================= +// * Encode Profile GUIDS supported by the NvEncodeAPI interface. +// ========================================================================================= + +// {BFD6F8E7-233C-4341-8B3E-4818523803F4} +static const GUID NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID = { + 0xbfd6f8e7, + 0x233c, + 0x4341, + {0x8b, 0x3e, 0x48, 0x18, 0x52, 0x38, 0x3, 0xf4}}; + +// {0727BCAA-78C4-4c83-8C2F-EF3DFF267C6A} +static const GUID NV_ENC_H264_PROFILE_BASELINE_GUID = { + 0x727bcaa, + 0x78c4, + 0x4c83, + {0x8c, 0x2f, 0xef, 0x3d, 0xff, 0x26, 0x7c, 0x6a}}; + +// {60B5C1D4-67FE-4790-94D5-C4726D7B6E6D} +static const GUID NV_ENC_H264_PROFILE_MAIN_GUID = { + 0x60b5c1d4, + 0x67fe, + 0x4790, + {0x94, 0xd5, 0xc4, 0x72, 0x6d, 0x7b, 0x6e, 0x6d}}; + +// {E7CBC309-4F7A-4b89-AF2A-D537C92BE310} +static const GUID NV_ENC_H264_PROFILE_HIGH_GUID = { + 0xe7cbc309, + 0x4f7a, + 0x4b89, + {0xaf, 0x2a, 0xd5, 0x37, 0xc9, 0x2b, 0xe3, 0x10}}; + +// {7AC663CB-A598-4960-B844-339B261A7D52} +static const GUID NV_ENC_H264_PROFILE_HIGH_444_GUID = { + 0x7ac663cb, + 0xa598, + 0x4960, + {0xb8, 0x44, 0x33, 0x9b, 0x26, 0x1a, 0x7d, 0x52}}; + +// {40847BF5-33F7-4601-9084-E8FE3C1DB8B7} +static const GUID NV_ENC_H264_PROFILE_STEREO_GUID = { + 0x40847bf5, + 0x33f7, + 0x4601, + {0x90, 0x84, 0xe8, 0xfe, 0x3c, 0x1d, 0xb8, 0xb7}}; + +// {B405AFAC-F32B-417B-89C4-9ABEED3E5978} +static const GUID NV_ENC_H264_PROFILE_PROGRESSIVE_HIGH_GUID = { + 0xb405afac, + 0xf32b, + 0x417b, + {0x89, 0xc4, 0x9a, 0xbe, 0xed, 0x3e, 0x59, 0x78}}; + +// {AEC1BD87-E85B-48f2-84C3-98BCA6285072} +static const GUID NV_ENC_H264_PROFILE_CONSTRAINED_HIGH_GUID = { + 0xaec1bd87, + 0xe85b, + 0x48f2, + {0x84, 0xc3, 0x98, 0xbc, 0xa6, 0x28, 0x50, 0x72}}; + +// {B514C39A-B55B-40fa-878F-F1253B4DFDEC} +static const GUID NV_ENC_HEVC_PROFILE_MAIN_GUID = { + 0xb514c39a, + 0xb55b, + 0x40fa, + {0x87, 0x8f, 0xf1, 0x25, 0x3b, 0x4d, 0xfd, 0xec}}; + +// {fa4d2b6c-3a5b-411a-8018-0a3f5e3c9be5} +static const GUID NV_ENC_HEVC_PROFILE_MAIN10_GUID = { + 0xfa4d2b6c, + 0x3a5b, + 0x411a, + {0x80, 0x18, 0x0a, 0x3f, 0x5e, 0x3c, 0x9b, 0xe5}}; + +// For HEVC Main 444 8 bit and HEVC Main 444 10 bit profiles only +// {51ec32b5-1b4c-453c-9cbd-b616bd621341} +static const GUID NV_ENC_HEVC_PROFILE_FREXT_GUID = { + 0x51ec32b5, + 0x1b4c, + 0x453c, + {0x9c, 0xbd, 0xb6, 0x16, 0xbd, 0x62, 0x13, 0x41}}; + +// ========================================================================================= +// * Preset GUIDS supported by the NvEncodeAPI interface. +// ========================================================================================= +// {B2DFB705-4EBD-4C49-9B5F-24A777D3E587} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_DEFAULT_GUID = { + 0xb2dfb705, + 0x4ebd, + 0x4c49, + {0x9b, 0x5f, 0x24, 0xa7, 0x77, 0xd3, 0xe5, 0x87}}; + +// {60E4C59F-E846-4484-A56D-CD45BE9FDDF6} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_HP_GUID = { + 0x60e4c59f, + 0xe846, + 0x4484, + {0xa5, 0x6d, 0xcd, 0x45, 0xbe, 0x9f, 0xdd, 0xf6}}; + +// {34DBA71D-A77B-4B8F-9C3E-B6D5DA24C012} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_HQ_GUID = { + 0x34dba71d, + 0xa77b, + 0x4b8f, + {0x9c, 0x3e, 0xb6, 0xd5, 0xda, 0x24, 0xc0, 0x12}}; + +// {82E3E450-BDBB-4e40-989C-82A90DF9EF32} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_BD_GUID = { + 0x82e3e450, + 0xbdbb, + 0x4e40, + {0x98, 0x9c, 0x82, 0xa9, 0xd, 0xf9, 0xef, 0x32}}; + +// {49DF21C5-6DFA-4feb-9787-6ACC9EFFB726} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID = { + 0x49df21c5, + 0x6dfa, + 0x4feb, + {0x97, 0x87, 0x6a, 0xcc, 0x9e, 0xff, 0xb7, 0x26}}; + +// {C5F733B9-EA97-4cf9-BEC2-BF78A74FD105} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOW_LATENCY_HQ_GUID = { + 0xc5f733b9, + 0xea97, + 0x4cf9, + {0xbe, 0xc2, 0xbf, 0x78, 0xa7, 0x4f, 0xd1, 0x5}}; + +// {67082A44-4BAD-48FA-98EA-93056D150A58} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOW_LATENCY_HP_GUID = { + 0x67082a44, 0x4bad, 0x48fa, {0x98, 0xea, 0x93, 0x5, 0x6d, 0x15, 0xa, 0x58}}; + +// {D5BFB716-C604-44e7-9BB8-DEA5510FC3AC} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOSSLESS_DEFAULT_GUID = { + 0xd5bfb716, + 0xc604, + 0x44e7, + {0x9b, 0xb8, 0xde, 0xa5, 0x51, 0xf, 0xc3, 0xac}}; + +// {149998E7-2364-411d-82EF-179888093409} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOSSLESS_HP_GUID = { + 0x149998e7, 0x2364, 0x411d, {0x82, 0xef, 0x17, 0x98, 0x88, 0x9, 0x34, 0x9}}; + +// Performance degrades and quality improves as we move from P1 to P7. Presets +// P3 to P7 for H264 and Presets P2 to P7 for HEVC have B frames enabled by +// default for HIGH_QUALITY and LOSSLESS tuning info, and will not work with +// Weighted Prediction enabled. In case Weighted Prediction is required, disable +// B frames by setting frameIntervalP = 1 {FC0A8D3E-45F8-4CF8-80C7-298871590EBF} +static const GUID NV_ENC_PRESET_P1_GUID = { + 0xfc0a8d3e, + 0x45f8, + 0x4cf8, + {0x80, 0xc7, 0x29, 0x88, 0x71, 0x59, 0xe, 0xbf}}; + +// {F581CFB8-88D6-4381-93F0-DF13F9C27DAB} +static const GUID NV_ENC_PRESET_P2_GUID = { + 0xf581cfb8, + 0x88d6, + 0x4381, + {0x93, 0xf0, 0xdf, 0x13, 0xf9, 0xc2, 0x7d, 0xab}}; + +// {36850110-3A07-441F-94D5-3670631F91F6} +static const GUID NV_ENC_PRESET_P3_GUID = { + 0x36850110, + 0x3a07, + 0x441f, + {0x94, 0xd5, 0x36, 0x70, 0x63, 0x1f, 0x91, 0xf6}}; + +// {90A7B826-DF06-4862-B9D2-CD6D73A08681} +static const GUID NV_ENC_PRESET_P4_GUID = { + 0x90a7b826, + 0xdf06, + 0x4862, + {0xb9, 0xd2, 0xcd, 0x6d, 0x73, 0xa0, 0x86, 0x81}}; + +// {21C6E6B4-297A-4CBA-998F-B6CBDE72ADE3} +static const GUID NV_ENC_PRESET_P5_GUID = { + 0x21c6e6b4, + 0x297a, + 0x4cba, + {0x99, 0x8f, 0xb6, 0xcb, 0xde, 0x72, 0xad, 0xe3}}; + +// {8E75C279-6299-4AB6-8302-0B215A335CF5} +static const GUID NV_ENC_PRESET_P6_GUID = { + 0x8e75c279, 0x6299, 0x4ab6, {0x83, 0x2, 0xb, 0x21, 0x5a, 0x33, 0x5c, 0xf5}}; + +// {84848C12-6F71-4C13-931B-53E283F57974} +static const GUID NV_ENC_PRESET_P7_GUID = { + 0x84848c12, + 0x6f71, + 0x4c13, + {0x93, 0x1b, 0x53, 0xe2, 0x83, 0xf5, 0x79, 0x74}}; + +/** + * \addtogroup ENCODER_STRUCTURE NvEncodeAPI Data structures + * @{ + */ + +/** + * Input frame encode modes + */ +typedef enum _NV_ENC_PARAMS_FRAME_FIELD_MODE { + NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME = 0x01, /**< Frame mode */ + NV_ENC_PARAMS_FRAME_FIELD_MODE_FIELD = 0x02, /**< Field mode */ + NV_ENC_PARAMS_FRAME_FIELD_MODE_MBAFF = 0x03 /**< MB adaptive frame/field */ +} NV_ENC_PARAMS_FRAME_FIELD_MODE; + +/** + * Rate Control Modes + */ +typedef enum _NV_ENC_PARAMS_RC_MODE { + NV_ENC_PARAMS_RC_CONSTQP = 0x0, /**< Constant QP mode */ + NV_ENC_PARAMS_RC_VBR = 0x1, /**< Variable bitrate mode */ + NV_ENC_PARAMS_RC_CBR = 0x2, /**< Constant bitrate mode */ + NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ = + 0x8, /**< Deprecated, use NV_ENC_PARAMS_RC_CBR + + NV_ENC_TWO_PASS_QUARTER_RESOLUTION / + NV_ENC_TWO_PASS_FULL_RESOLUTION + lowDelayKeyFrameScale=1 */ + NV_ENC_PARAMS_RC_CBR_HQ = 0x10, /**< Deprecated, use NV_ENC_PARAMS_RC_CBR + + NV_ENC_TWO_PASS_QUARTER_RESOLUTION / + NV_ENC_TWO_PASS_FULL_RESOLUTION */ + NV_ENC_PARAMS_RC_VBR_HQ = 0x20 /**< Deprecated, use NV_ENC_PARAMS_RC_VBR + + NV_ENC_TWO_PASS_QUARTER_RESOLUTION / + NV_ENC_TWO_PASS_FULL_RESOLUTION */ +} NV_ENC_PARAMS_RC_MODE; + +/** + * Multi Pass encoding + */ +typedef enum _NV_ENC_MULTI_PASS { + NV_ENC_MULTI_PASS_DISABLED = 0x0, /**< Single Pass */ + NV_ENC_TWO_PASS_QUARTER_RESOLUTION = + 0x1, /**< Two Pass encoding is enabled where first Pass is quarter + resolution */ + NV_ENC_TWO_PASS_FULL_RESOLUTION = 0x2, /**< Two Pass encoding is enabled where + first Pass is full resolution */ +} NV_ENC_MULTI_PASS; + +/** + * Emphasis Levels + */ +typedef enum _NV_ENC_EMPHASIS_MAP_LEVEL { + NV_ENC_EMPHASIS_MAP_LEVEL_0 = + 0x0, /**< Emphasis Map Level 0, for zero Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_1 = + 0x1, /**< Emphasis Map Level 1, for very low Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_2 = + 0x2, /**< Emphasis Map Level 2, for low Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_3 = + 0x3, /**< Emphasis Map Level 3, for medium Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_4 = + 0x4, /**< Emphasis Map Level 4, for high Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_5 = + 0x5 /**< Emphasis Map Level 5, for very high Delta QP value */ +} NV_ENC_EMPHASIS_MAP_LEVEL; + +/** + * QP MAP MODE + */ +typedef enum _NV_ENC_QP_MAP_MODE { + NV_ENC_QP_MAP_DISABLED = + 0x0, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap have no effect. */ + NV_ENC_QP_MAP_EMPHASIS = + 0x1, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap will be treated as + Emphasis level. Currently this is only supported for H264 */ + NV_ENC_QP_MAP_DELTA = 0x2, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap will be + treated as QP delta map. */ + NV_ENC_QP_MAP = + 0x3, /**< Currently This is not supported. Value in + NV_ENC_PIC_PARAMS::qpDeltaMap will be treated as QP value. */ +} NV_ENC_QP_MAP_MODE; + +#define NV_ENC_PARAMS_RC_VBR_MINQP \ + (NV_ENC_PARAMS_RC_MODE)0x4 /**< Deprecated \ + */ +#define NV_ENC_PARAMS_RC_2_PASS_QUALITY \ + NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ /**< Deprecated */ +#define NV_ENC_PARAMS_RC_2_PASS_FRAMESIZE_CAP \ + NV_ENC_PARAMS_RC_CBR_HQ /**< Deprecated */ +#define NV_ENC_PARAMS_RC_2_PASS_VBR NV_ENC_PARAMS_RC_VBR_HQ /**< Deprecated */ +#define NV_ENC_PARAMS_RC_CBR2 NV_ENC_PARAMS_RC_CBR /**< Deprecated */ + +/** + * Input picture structure + */ +typedef enum _NV_ENC_PIC_STRUCT { + NV_ENC_PIC_STRUCT_FRAME = 0x01, /**< Progressive frame */ + NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM = + 0x02, /**< Field encoding top field first */ + NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP = + 0x03 /**< Field encoding bottom field first */ +} NV_ENC_PIC_STRUCT; + +/** + * Input picture type + */ +typedef enum _NV_ENC_PIC_TYPE { + NV_ENC_PIC_TYPE_P = 0x0, /**< Forward predicted */ + NV_ENC_PIC_TYPE_B = 0x01, /**< Bi-directionally predicted picture */ + NV_ENC_PIC_TYPE_I = 0x02, /**< Intra predicted picture */ + NV_ENC_PIC_TYPE_IDR = 0x03, /**< IDR picture */ + NV_ENC_PIC_TYPE_BI = + 0x04, /**< Bi-directionally predicted with only Intra MBs */ + NV_ENC_PIC_TYPE_SKIPPED = 0x05, /**< Picture is skipped */ + NV_ENC_PIC_TYPE_INTRA_REFRESH = + 0x06, /**< First picture in intra refresh cycle */ + NV_ENC_PIC_TYPE_NONREF_P = 0x07, /**< Non reference P picture */ + NV_ENC_PIC_TYPE_UNKNOWN = 0xFF /**< Picture type unknown */ +} NV_ENC_PIC_TYPE; + +/** + * Motion vector precisions + */ +typedef enum _NV_ENC_MV_PRECISION { + NV_ENC_MV_PRECISION_DEFAULT = + 0x0, /**< Driver selects Quarter-Pel motion vector precision by default */ + NV_ENC_MV_PRECISION_FULL_PEL = 0x01, /**< Full-Pel motion vector precision */ + NV_ENC_MV_PRECISION_HALF_PEL = 0x02, /**< Half-Pel motion vector precision */ + NV_ENC_MV_PRECISION_QUARTER_PEL = + 0x03 /**< Quarter-Pel motion vector precision */ +} NV_ENC_MV_PRECISION; + +/** + * Input buffer formats + */ +typedef enum _NV_ENC_BUFFER_FORMAT { + NV_ENC_BUFFER_FORMAT_UNDEFINED = 0x00000000, /**< Undefined buffer format */ + + NV_ENC_BUFFER_FORMAT_NV12 = 0x00000001, /**< Semi-Planar YUV [Y plane followed + by interleaved UV plane] */ + NV_ENC_BUFFER_FORMAT_YV12 = + 0x00000010, /**< Planar YUV [Y plane followed by V and U planes] */ + NV_ENC_BUFFER_FORMAT_IYUV = + 0x00000100, /**< Planar YUV [Y plane followed by U and V planes] */ + NV_ENC_BUFFER_FORMAT_YUV444 = + 0x00001000, /**< Planar YUV [Y plane followed by U and V planes] */ + NV_ENC_BUFFER_FORMAT_YUV420_10BIT = + 0x00010000, /**< 10 bit Semi-Planar YUV [Y plane followed by interleaved + UV plane]. Each pixel of size 2 bytes. Most Significant 10 + bits contain pixel data. */ + NV_ENC_BUFFER_FORMAT_YUV444_10BIT = + 0x00100000, /**< 10 bit Planar YUV444 [Y plane followed by U and V + planes]. Each pixel of size 2 bytes. Most Significant 10 + bits contain pixel data. */ + NV_ENC_BUFFER_FORMAT_ARGB = + 0x01000000, /**< 8 bit Packed A8R8G8B8. This is a word-ordered format + where a pixel is represented by a 32-bit word with B + in the lowest 8 bits, G in the next 8 bits, R in the + 8 bits after that and A in the highest 8 bits. */ + NV_ENC_BUFFER_FORMAT_ARGB10 = + 0x02000000, /**< 10 bit Packed A2R10G10B10. This is a word-ordered format + where a pixel is represented by a 32-bit word with B + in the lowest 10 bits, G in the next 10 bits, R in the + 10 bits after that and A in the highest 2 bits. */ + NV_ENC_BUFFER_FORMAT_AYUV = + 0x04000000, /**< 8 bit Packed A8Y8U8V8. This is a word-ordered format + where a pixel is represented by a 32-bit word with V + in the lowest 8 bits, U in the next 8 bits, Y in the + 8 bits after that and A in the highest 8 bits. */ + NV_ENC_BUFFER_FORMAT_ABGR = + 0x10000000, /**< 8 bit Packed A8B8G8R8. This is a word-ordered format + where a pixel is represented by a 32-bit word with R + in the lowest 8 bits, G in the next 8 bits, B in the + 8 bits after that and A in the highest 8 bits. */ + NV_ENC_BUFFER_FORMAT_ABGR10 = + 0x20000000, /**< 10 bit Packed A2B10G10R10. This is a word-ordered format + where a pixel is represented by a 32-bit word with R + in the lowest 10 bits, G in the next 10 bits, B in the + 10 bits after that and A in the highest 2 bits. */ + NV_ENC_BUFFER_FORMAT_U8 = + 0x40000000, /**< Buffer format representing one-dimensional buffer. + This format should be used only when registering the + resource as output buffer, which will be used to write + the encoded bit stream or H.264 ME only mode output. */ +} NV_ENC_BUFFER_FORMAT; + +#define NV_ENC_BUFFER_FORMAT_NV12_PL NV_ENC_BUFFER_FORMAT_NV12 +#define NV_ENC_BUFFER_FORMAT_YV12_PL NV_ENC_BUFFER_FORMAT_YV12 +#define NV_ENC_BUFFER_FORMAT_IYUV_PL NV_ENC_BUFFER_FORMAT_IYUV +#define NV_ENC_BUFFER_FORMAT_YUV444_PL NV_ENC_BUFFER_FORMAT_YUV444 + +/** + * Encoding levels + */ +typedef enum _NV_ENC_LEVEL { + NV_ENC_LEVEL_AUTOSELECT = 0, + + NV_ENC_LEVEL_H264_1 = 10, + NV_ENC_LEVEL_H264_1b = 9, + NV_ENC_LEVEL_H264_11 = 11, + NV_ENC_LEVEL_H264_12 = 12, + NV_ENC_LEVEL_H264_13 = 13, + NV_ENC_LEVEL_H264_2 = 20, + NV_ENC_LEVEL_H264_21 = 21, + NV_ENC_LEVEL_H264_22 = 22, + NV_ENC_LEVEL_H264_3 = 30, + NV_ENC_LEVEL_H264_31 = 31, + NV_ENC_LEVEL_H264_32 = 32, + NV_ENC_LEVEL_H264_4 = 40, + NV_ENC_LEVEL_H264_41 = 41, + NV_ENC_LEVEL_H264_42 = 42, + NV_ENC_LEVEL_H264_5 = 50, + NV_ENC_LEVEL_H264_51 = 51, + NV_ENC_LEVEL_H264_52 = 52, + NV_ENC_LEVEL_H264_60 = 60, + NV_ENC_LEVEL_H264_61 = 61, + NV_ENC_LEVEL_H264_62 = 62, + + NV_ENC_LEVEL_HEVC_1 = 30, + NV_ENC_LEVEL_HEVC_2 = 60, + NV_ENC_LEVEL_HEVC_21 = 63, + NV_ENC_LEVEL_HEVC_3 = 90, + NV_ENC_LEVEL_HEVC_31 = 93, + NV_ENC_LEVEL_HEVC_4 = 120, + NV_ENC_LEVEL_HEVC_41 = 123, + NV_ENC_LEVEL_HEVC_5 = 150, + NV_ENC_LEVEL_HEVC_51 = 153, + NV_ENC_LEVEL_HEVC_52 = 156, + NV_ENC_LEVEL_HEVC_6 = 180, + NV_ENC_LEVEL_HEVC_61 = 183, + NV_ENC_LEVEL_HEVC_62 = 186, + + NV_ENC_TIER_HEVC_MAIN = 0, + NV_ENC_TIER_HEVC_HIGH = 1 +} NV_ENC_LEVEL; + +/** + * Error Codes + */ +typedef enum _NVENCSTATUS { + /** + * This indicates that API call returned with no errors. + */ + NV_ENC_SUCCESS, + + /** + * This indicates that no encode capable devices were detected. + */ + NV_ENC_ERR_NO_ENCODE_DEVICE, + + /** + * This indicates that devices pass by the client is not supported. + */ + NV_ENC_ERR_UNSUPPORTED_DEVICE, + + /** + * This indicates that the encoder device supplied by the client is not + * valid. + */ + NV_ENC_ERR_INVALID_ENCODERDEVICE, + + /** + * This indicates that device passed to the API call is invalid. + */ + NV_ENC_ERR_INVALID_DEVICE, + + /** + * This indicates that device passed to the API call is no longer available + * and needs to be reinitialized. The clients need to destroy the current + * encoder session by freeing the allocated input output buffers and + * destroying the device and create a new encoding session. + */ + NV_ENC_ERR_DEVICE_NOT_EXIST, + + /** + * This indicates that one or more of the pointers passed to the API call + * is invalid. + */ + NV_ENC_ERR_INVALID_PTR, + + /** + * This indicates that completion event passed in ::NvEncEncodePicture() call + * is invalid. + */ + NV_ENC_ERR_INVALID_EVENT, + + /** + * This indicates that one or more of the parameter passed to the API call + * is invalid. + */ + NV_ENC_ERR_INVALID_PARAM, + + /** + * This indicates that an API call was made in wrong sequence/order. + */ + NV_ENC_ERR_INVALID_CALL, + + /** + * This indicates that the API call failed because it was unable to allocate + * enough memory to perform the requested operation. + */ + NV_ENC_ERR_OUT_OF_MEMORY, + + /** + * This indicates that the encoder has not been initialized with + * ::NvEncInitializeEncoder() or that initialization has failed. + * The client cannot allocate input or output buffers or do any encoding + * related operation before successfully initializing the encoder. + */ + NV_ENC_ERR_ENCODER_NOT_INITIALIZED, + + /** + * This indicates that an unsupported parameter was passed by the client. + */ + NV_ENC_ERR_UNSUPPORTED_PARAM, + + /** + * This indicates that the ::NvEncLockBitstream() failed to lock the output + * buffer. This happens when the client makes a non blocking lock call to + * access the output bitstream by passing NV_ENC_LOCK_BITSTREAM::doNotWait + * flag. This is not a fatal error and client should retry the same operation + * after few milliseconds. + */ + NV_ENC_ERR_LOCK_BUSY, + + /** + * This indicates that the size of the user buffer passed by the client is + * insufficient for the requested operation. + */ + NV_ENC_ERR_NOT_ENOUGH_BUFFER, + + /** + * This indicates that an invalid struct version was used by the client. + */ + NV_ENC_ERR_INVALID_VERSION, + + /** + * This indicates that ::NvEncMapInputResource() API failed to map the client + * provided input resource. + */ + NV_ENC_ERR_MAP_FAILED, + + /** + * This indicates encode driver requires more input buffers to produce an + * output bitstream. If this error is returned from ::NvEncEncodePicture() + * API, this is not a fatal error. If the client is encoding with B frames + * then, + * ::NvEncEncodePicture() API might be buffering the input frame for + * re-ordering. + * + * A client operating in synchronous mode cannot call ::NvEncLockBitstream() + * API on the output bitstream buffer if ::NvEncEncodePicture() returned the + * ::NV_ENC_ERR_NEED_MORE_INPUT error code. + * The client must continue providing input frames until encode driver returns + * ::NV_ENC_SUCCESS. After receiving ::NV_ENC_SUCCESS status the client can + * call + * ::NvEncLockBitstream() API on the output buffers in the same order in which + * it has called ::NvEncEncodePicture(). + */ + NV_ENC_ERR_NEED_MORE_INPUT, + + /** + * This indicates that the HW encoder is busy encoding and is unable to encode + * the input. The client should call ::NvEncEncodePicture() again after few + * milliseconds. + */ + NV_ENC_ERR_ENCODER_BUSY, + + /** + * This indicates that the completion event passed in ::NvEncEncodePicture() + * API has not been registered with encoder driver using + * ::NvEncRegisterAsyncEvent(). + */ + NV_ENC_ERR_EVENT_NOT_REGISTERD, + + /** + * This indicates that an unknown internal error has occurred. + */ + NV_ENC_ERR_GENERIC, + + /** + * This indicates that the client is attempting to use a feature + * that is not available for the license type for the current system. + */ + NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY, + + /** + * This indicates that the client is attempting to use a feature + * that is not implemented for the current version. + */ + NV_ENC_ERR_UNIMPLEMENTED, + + /** + * This indicates that the ::NvEncRegisterResource API failed to register the + * resource. + */ + NV_ENC_ERR_RESOURCE_REGISTER_FAILED, + + /** + * This indicates that the client is attempting to unregister a resource + * that has not been successfully registered. + */ + NV_ENC_ERR_RESOURCE_NOT_REGISTERED, + + /** + * This indicates that the client is attempting to unmap a resource + * that has not been successfully mapped. + */ + NV_ENC_ERR_RESOURCE_NOT_MAPPED, + +} NVENCSTATUS; + +/** + * Encode Picture encode flags. + */ +typedef enum _NV_ENC_PIC_FLAGS { + NV_ENC_PIC_FLAG_FORCEINTRA = + 0x1, /**< Encode the current picture as an Intra picture */ + NV_ENC_PIC_FLAG_FORCEIDR = + 0x2, /**< Encode the current picture as an IDR picture. + This flag is only valid when Picture type decision is taken by + the Encoder + [_NV_ENC_INITIALIZE_PARAMS::enablePTD == 1]. */ + NV_ENC_PIC_FLAG_OUTPUT_SPSPPS = + 0x4, /**< Write the sequence and picture header in encoded bitstream of + the current picture */ + NV_ENC_PIC_FLAG_EOS = 0x8, /**< Indicates end of the input stream */ +} NV_ENC_PIC_FLAGS; + +/** + * Memory heap to allocate input and output buffers. + */ +typedef enum _NV_ENC_MEMORY_HEAP { + NV_ENC_MEMORY_HEAP_AUTOSELECT = 0, /**< Memory heap to be decided by the + encoder driver based on the usage */ + NV_ENC_MEMORY_HEAP_VID = 1, /**< Memory heap is in local video memory */ + NV_ENC_MEMORY_HEAP_SYSMEM_CACHED = + 2, /**< Memory heap is in cached system memory */ + NV_ENC_MEMORY_HEAP_SYSMEM_UNCACHED = + 3 /**< Memory heap is in uncached system memory */ +} NV_ENC_MEMORY_HEAP; + +/** + * B-frame used as reference modes + */ +typedef enum _NV_ENC_BFRAME_REF_MODE { + NV_ENC_BFRAME_REF_MODE_DISABLED = + 0x0, /**< B frame is not used for reference */ + NV_ENC_BFRAME_REF_MODE_EACH = + 0x1, /**< Each B-frame will be used for reference. currently not supported + for H.264 */ + NV_ENC_BFRAME_REF_MODE_MIDDLE = 0x2, /**< Only(Number of B-frame)/2 th B-frame + will be used for reference */ +} NV_ENC_BFRAME_REF_MODE; + +/** + * H.264 entropy coding modes. + */ +typedef enum _NV_ENC_H264_ENTROPY_CODING_MODE { + NV_ENC_H264_ENTROPY_CODING_MODE_AUTOSELECT = + 0x0, /**< Entropy coding mode is auto selected by the encoder driver */ + NV_ENC_H264_ENTROPY_CODING_MODE_CABAC = + 0x1, /**< Entropy coding mode is CABAC */ + NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC = + 0x2 /**< Entropy coding mode is CAVLC */ +} NV_ENC_H264_ENTROPY_CODING_MODE; + +/** + * H.264 specific BDirect modes + */ +typedef enum _NV_ENC_H264_BDIRECT_MODE { + NV_ENC_H264_BDIRECT_MODE_AUTOSELECT = + 0x0, /**< BDirect mode is auto selected by the encoder driver */ + NV_ENC_H264_BDIRECT_MODE_DISABLE = 0x1, /**< Disable BDirect mode */ + NV_ENC_H264_BDIRECT_MODE_TEMPORAL = 0x2, /**< Temporal BDirect mode */ + NV_ENC_H264_BDIRECT_MODE_SPATIAL = 0x3 /**< Spatial BDirect mode */ +} NV_ENC_H264_BDIRECT_MODE; + +/** + * H.264 specific FMO usage + */ +typedef enum _NV_ENC_H264_FMO_MODE { + NV_ENC_H264_FMO_AUTOSELECT = + 0x0, /**< FMO usage is auto selected by the encoder driver */ + NV_ENC_H264_FMO_ENABLE = 0x1, /**< Enable FMO */ + NV_ENC_H264_FMO_DISABLE = 0x2, /**< Disable FMO */ +} NV_ENC_H264_FMO_MODE; + +/** + * H.264 specific Adaptive Transform modes + */ +typedef enum _NV_ENC_H264_ADAPTIVE_TRANSFORM_MODE { + NV_ENC_H264_ADAPTIVE_TRANSFORM_AUTOSELECT = + 0x0, /**< Adaptive Transform 8x8 mode is auto selected by the encoder + driver*/ + NV_ENC_H264_ADAPTIVE_TRANSFORM_DISABLE = + 0x1, /**< Adaptive Transform 8x8 mode disabled */ + NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE = + 0x2, /**< Adaptive Transform 8x8 mode should be used */ +} NV_ENC_H264_ADAPTIVE_TRANSFORM_MODE; + +/** + * Stereo frame packing modes. + */ +typedef enum _NV_ENC_STEREO_PACKING_MODE { + NV_ENC_STEREO_PACKING_MODE_NONE = 0x0, /**< No Stereo packing required */ + NV_ENC_STEREO_PACKING_MODE_CHECKERBOARD = + 0x1, /**< Checkerboard mode for packing stereo frames */ + NV_ENC_STEREO_PACKING_MODE_COLINTERLEAVE = + 0x2, /**< Column Interleave mode for packing stereo frames */ + NV_ENC_STEREO_PACKING_MODE_ROWINTERLEAVE = + 0x3, /**< Row Interleave mode for packing stereo frames */ + NV_ENC_STEREO_PACKING_MODE_SIDEBYSIDE = + 0x4, /**< Side-by-side mode for packing stereo frames */ + NV_ENC_STEREO_PACKING_MODE_TOPBOTTOM = + 0x5, /**< Top-Bottom mode for packing stereo frames */ + NV_ENC_STEREO_PACKING_MODE_FRAMESEQ = + 0x6 /**< Frame Sequential mode for packing stereo frames */ +} NV_ENC_STEREO_PACKING_MODE; + +/** + * Input Resource type + */ +typedef enum _NV_ENC_INPUT_RESOURCE_TYPE { + NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX = + 0x0, /**< input resource type is a directx9 surface*/ + NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR = + 0x1, /**< input resource type is a cuda device pointer surface*/ + NV_ENC_INPUT_RESOURCE_TYPE_CUDAARRAY = + 0x2, /**< input resource type is a cuda array surface. + This array must be a 2D array and the CUDA_ARRAY3D_SURFACE_LDST + flag must have been specified when creating it. */ + NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX = + 0x3 /**< input resource type is an OpenGL texture */ +} NV_ENC_INPUT_RESOURCE_TYPE; + +/** + * Buffer usage + */ +typedef enum _NV_ENC_BUFFER_USAGE { + NV_ENC_INPUT_IMAGE = + 0x0, /**< Registered surface will be used for input image */ + NV_ENC_OUTPUT_MOTION_VECTOR = + 0x1, /**< Registered surface will be used for output of H.264 ME only + mode. This buffer usage type is not supported for HEVC ME only + mode. */ + NV_ENC_OUTPUT_BITSTREAM = 0x2 /**< Registered surface will be used for output + bitstream in encoding */ +} NV_ENC_BUFFER_USAGE; + +/** + * Encoder Device type + */ +typedef enum _NV_ENC_DEVICE_TYPE { + NV_ENC_DEVICE_TYPE_DIRECTX = + 0x0, /**< encode device type is a directx9 device */ + NV_ENC_DEVICE_TYPE_CUDA = 0x1, /**< encode device type is a cuda device */ + NV_ENC_DEVICE_TYPE_OPENGL = + 0x2 /**< encode device type is an OpenGL device. + Use of this device type is supported only on Linux */ +} NV_ENC_DEVICE_TYPE; + +/** + * Number of reference frames + */ +typedef enum _NV_ENC_NUM_REF_FRAMES { + NV_ENC_NUM_REF_FRAMES_AUTOSELECT = + 0x0, /**< Number of reference frames is auto selected by the encoder + driver */ + NV_ENC_NUM_REF_FRAMES_1 = 0x1, /**< Number of reference frames equal to 1 */ + NV_ENC_NUM_REF_FRAMES_2 = 0x2, /**< Number of reference frames equal to 2 */ + NV_ENC_NUM_REF_FRAMES_3 = 0x3, /**< Number of reference frames equal to 3 */ + NV_ENC_NUM_REF_FRAMES_4 = 0x4, /**< Number of reference frames equal to 4 */ + NV_ENC_NUM_REF_FRAMES_5 = 0x5, /**< Number of reference frames equal to 5 */ + NV_ENC_NUM_REF_FRAMES_6 = 0x6, /**< Number of reference frames equal to 6 */ + NV_ENC_NUM_REF_FRAMES_7 = 0x7 /**< Number of reference frames equal to 7 */ +} NV_ENC_NUM_REF_FRAMES; + +/** + * Encoder capabilities enumeration. + */ +typedef enum _NV_ENC_CAPS { + /** + * Maximum number of B-Frames supported. + */ + NV_ENC_CAPS_NUM_MAX_BFRAMES, + + /** + * Rate control modes supported. + * \n The API return value is a bitmask of the values in + * NV_ENC_PARAMS_RC_MODE. + */ + NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES, + + /** + * Indicates HW support for field mode encoding. + * \n 0 : Interlaced mode encoding is not supported. + * \n 1 : Interlaced field mode encoding is supported. + * \n 2 : Interlaced frame encoding and field mode encoding are both + * supported. + */ + NV_ENC_CAPS_SUPPORT_FIELD_ENCODING, + + /** + * Indicates HW support for monochrome mode encoding. + * \n 0 : Monochrome mode not supported. + * \n 1 : Monochrome mode supported. + */ + NV_ENC_CAPS_SUPPORT_MONOCHROME, + + /** + * Indicates HW support for FMO. + * \n 0 : FMO not supported. + * \n 1 : FMO supported. + */ + NV_ENC_CAPS_SUPPORT_FMO, + + /** + * Indicates HW capability for Quarter pel motion estimation. + * \n 0 : Quarter-Pel Motion Estimation not supported. + * \n 1 : Quarter-Pel Motion Estimation supported. + */ + NV_ENC_CAPS_SUPPORT_QPELMV, + + /** + * H.264 specific. Indicates HW support for BDirect modes. + * \n 0 : BDirect mode encoding not supported. + * \n 1 : BDirect mode encoding supported. + */ + NV_ENC_CAPS_SUPPORT_BDIRECT_MODE, + + /** + * H264 specific. Indicates HW support for CABAC entropy coding mode. + * \n 0 : CABAC entropy coding not supported. + * \n 1 : CABAC entropy coding supported. + */ + NV_ENC_CAPS_SUPPORT_CABAC, + + /** + * Indicates HW support for Adaptive Transform. + * \n 0 : Adaptive Transform not supported. + * \n 1 : Adaptive Transform supported. + */ + NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM, + + /** + * Indicates HW support for Multi View Coding. + * \n 0 : Multi View Coding not supported. + * \n 1 : Multi View Coding supported. + */ + NV_ENC_CAPS_SUPPORT_STEREO_MVC, + + /** + * Indicates HW support for encoding Temporal layers. + * \n 0 : Encoding Temporal layers not supported. + * \n 1 : Encoding Temporal layers supported. + */ + NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS, + + /** + * Indicates HW support for Hierarchical P frames. + * \n 0 : Hierarchical P frames not supported. + * \n 1 : Hierarchical P frames supported. + */ + NV_ENC_CAPS_SUPPORT_HIERARCHICAL_PFRAMES, + + /** + * Indicates HW support for Hierarchical B frames. + * \n 0 : Hierarchical B frames not supported. + * \n 1 : Hierarchical B frames supported. + */ + NV_ENC_CAPS_SUPPORT_HIERARCHICAL_BFRAMES, + + /** + * Maximum Encoding level supported (See ::NV_ENC_LEVEL for details). + */ + NV_ENC_CAPS_LEVEL_MAX, + + /** + * Minimum Encoding level supported (See ::NV_ENC_LEVEL for details). + */ + NV_ENC_CAPS_LEVEL_MIN, + + /** + * Indicates HW support for separate colour plane encoding. + * \n 0 : Separate colour plane encoding not supported. + * \n 1 : Separate colour plane encoding supported. + */ + NV_ENC_CAPS_SEPARATE_COLOUR_PLANE, + + /** + * Maximum output width supported. + */ + NV_ENC_CAPS_WIDTH_MAX, + + /** + * Maximum output height supported. + */ + NV_ENC_CAPS_HEIGHT_MAX, + + /** + * Indicates Temporal Scalability Support. + * \n 0 : Temporal SVC encoding not supported. + * \n 1 : Temporal SVC encoding supported. + */ + NV_ENC_CAPS_SUPPORT_TEMPORAL_SVC, + + /** + * Indicates Dynamic Encode Resolution Change Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Dynamic Encode Resolution Change not supported. + * \n 1 : Dynamic Encode Resolution Change supported. + */ + NV_ENC_CAPS_SUPPORT_DYN_RES_CHANGE, + + /** + * Indicates Dynamic Encode Bitrate Change Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Dynamic Encode bitrate change not supported. + * \n 1 : Dynamic Encode bitrate change supported. + */ + NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE, + + /** + * Indicates Forcing Constant QP On The Fly Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Forcing constant QP on the fly not supported. + * \n 1 : Forcing constant QP on the fly supported. + */ + NV_ENC_CAPS_SUPPORT_DYN_FORCE_CONSTQP, + + /** + * Indicates Dynamic rate control mode Change Support. + * \n 0 : Dynamic rate control mode change not supported. + * \n 1 : Dynamic rate control mode change supported. + */ + NV_ENC_CAPS_SUPPORT_DYN_RCMODE_CHANGE, + + /** + * Indicates Subframe readback support for slice-based encoding. If this + * feature is supported, it can be enabled by setting enableSubFrameWrite = 1. + * \n 0 : Subframe readback not supported. + * \n 1 : Subframe readback supported. + */ + NV_ENC_CAPS_SUPPORT_SUBFRAME_READBACK, + + /** + * Indicates Constrained Encoding mode support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Constrained encoding mode not supported. + * \n 1 : Constrained encoding mode supported. + * If this mode is supported client can enable this during initialization. + * Client can then force a picture to be coded as constrained picture where + * in-loop filtering is disabled across slice boundaries and prediction + * vectors for inter macroblocks in each slice will be restricted to the slice + * region. + */ + NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING, + + /** + * Indicates Intra Refresh Mode Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Intra Refresh Mode not supported. + * \n 1 : Intra Refresh Mode supported. + */ + NV_ENC_CAPS_SUPPORT_INTRA_REFRESH, + + /** + * Indicates Custom VBV Buffer Size support. It can be used for capping frame + * size. Support added from NvEncodeAPI version 2.0. \n 0 : Custom VBV buffer + * size specification from client, not supported. \n 1 : Custom VBV buffer + * size specification from client, supported. + */ + NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE, + + /** + * Indicates Dynamic Slice Mode Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Dynamic Slice Mode not supported. + * \n 1 : Dynamic Slice Mode supported. + */ + NV_ENC_CAPS_SUPPORT_DYNAMIC_SLICE_MODE, + + /** + * Indicates Reference Picture Invalidation Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Reference Picture Invalidation not supported. + * \n 1 : Reference Picture Invalidation supported. + */ + NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION, + + /** + * Indicates support for Pre-Processing. + * The API return value is a bitmask of the values defined in + * ::NV_ENC_PREPROC_FLAGS + */ + NV_ENC_CAPS_PREPROC_SUPPORT, + + /** + * Indicates support Async mode. + * \n 0 : Async Encode mode not supported. + * \n 1 : Async Encode mode supported. + */ + NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT, + + /** + * Maximum MBs per frame supported. + */ + NV_ENC_CAPS_MB_NUM_MAX, + + /** + * Maximum aggregate throughput in MBs per sec. + */ + NV_ENC_CAPS_MB_PER_SEC_MAX, + + /** + * Indicates HW support for YUV444 mode encoding. + * \n 0 : YUV444 mode encoding not supported. + * \n 1 : YUV444 mode encoding supported. + */ + NV_ENC_CAPS_SUPPORT_YUV444_ENCODE, + + /** + * Indicates HW support for lossless encoding. + * \n 0 : lossless encoding not supported. + * \n 1 : lossless encoding supported. + */ + NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE, + + /** + * Indicates HW support for Sample Adaptive Offset. + * \n 0 : SAO not supported. + * \n 1 : SAO encoding supported. + */ + NV_ENC_CAPS_SUPPORT_SAO, + + /** + * Indicates HW support for Motion Estimation Only Mode. + * \n 0 : MEOnly Mode not supported. + * \n 1 : MEOnly Mode supported for I and P frames. + * \n 2 : MEOnly Mode supported for I, P and B frames. + */ + NV_ENC_CAPS_SUPPORT_MEONLY_MODE, + + /** + * Indicates HW support for lookahead encoding (enableLookahead=1). + * \n 0 : Lookahead not supported. + * \n 1 : Lookahead supported. + */ + NV_ENC_CAPS_SUPPORT_LOOKAHEAD, + + /** + * Indicates HW support for temporal AQ encoding (enableTemporalAQ=1). + * \n 0 : Temporal AQ not supported. + * \n 1 : Temporal AQ supported. + */ + NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ, + /** + * Indicates HW support for 10 bit encoding. + * \n 0 : 10 bit encoding not supported. + * \n 1 : 10 bit encoding supported. + */ + NV_ENC_CAPS_SUPPORT_10BIT_ENCODE, + /** + * Maximum number of Long Term Reference frames supported + */ + NV_ENC_CAPS_NUM_MAX_LTR_FRAMES, + + /** + * Indicates HW support for Weighted Prediction. + * \n 0 : Weighted Prediction not supported. + * \n 1 : Weighted Prediction supported. + */ + NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION, + + /** + * On managed (vGPU) platforms (Windows only), this API, in conjunction with + * other GRID Management APIs, can be used to estimate the residual capacity + * of the hardware encoder on the GPU as a percentage of the total available + * encoder capacity. This API can be called at any time; i.e. during the + * encode session or before opening the encode session. If the available + * encoder capacity is returned as zero, applications may choose to switch to + * software encoding and continue to call this API (e.g. polling once per + * second) until capacity becomes available. + * + * On bare metal (non-virtualized GPU) and linux platforms, this API always + * returns 100. + */ + NV_ENC_CAPS_DYNAMIC_QUERY_ENCODER_CAPACITY, + + /** + * Indicates B as reference support. + * \n 0 : B as reference is not supported. + * \n 1 : each B-Frame as reference is supported. + * \n 2 : only Middle B-frame as reference is supported. + */ + NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE, + + /** + * Indicates HW support for Emphasis Level Map based delta QP computation. + * \n 0 : Emphasis Level Map based delta QP not supported. + * \n 1 : Emphasis Level Map based delta QP is supported. + */ + NV_ENC_CAPS_SUPPORT_EMPHASIS_LEVEL_MAP, + + /** + * Minimum input width supported. + */ + NV_ENC_CAPS_WIDTH_MIN, + + /** + * Minimum input height supported. + */ + NV_ENC_CAPS_HEIGHT_MIN, + + /** + * Indicates HW support for multiple reference frames. + */ + NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES, + + /** + * Indicates HW support for HEVC with alpha encoding. + * \n 0 : HEVC with alpha encoding not supported. + * \n 1 : HEVC with alpha encoding is supported. + */ + NV_ENC_CAPS_SUPPORT_ALPHA_LAYER_ENCODING, + + /** + * Indicates number of Encoding engines present on GPU. + */ + NV_ENC_CAPS_NUM_ENCODER_ENGINES, + + /** + * Reserved - Not to be used by clients. + */ + NV_ENC_CAPS_EXPOSED_COUNT +} NV_ENC_CAPS; + +/** + * HEVC CU SIZE + */ +typedef enum _NV_ENC_HEVC_CUSIZE { + NV_ENC_HEVC_CUSIZE_AUTOSELECT = 0, + NV_ENC_HEVC_CUSIZE_8x8 = 1, + NV_ENC_HEVC_CUSIZE_16x16 = 2, + NV_ENC_HEVC_CUSIZE_32x32 = 3, + NV_ENC_HEVC_CUSIZE_64x64 = 4, +} NV_ENC_HEVC_CUSIZE; + +/** + * Input struct for querying Encoding capabilities. + */ +typedef struct _NV_ENC_CAPS_PARAM { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_CAPS_PARAM_VER */ + NV_ENC_CAPS + capsToQuery; /**< [in]: Specifies the encode capability to be queried. + Client should pass a member for ::NV_ENC_CAPS enum. */ + uint32_t reserved[62]; /**< [in]: Reserved and must be set to 0 */ +} NV_ENC_CAPS_PARAM; + +/** NV_ENC_CAPS_PARAM struct version. */ +#define NV_ENC_CAPS_PARAM_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * Encoder Output parameters + */ +typedef struct _NV_ENC_ENCODE_OUT_PARAMS { + uint32_t version; /**< [out]: Struct version. */ + uint32_t bitstreamSizeInBytes; /**< [out]: Encoded bitstream size in bytes */ + uint32_t reserved[62]; /**< [out]: Reserved and must be set to 0 */ +} NV_ENC_ENCODE_OUT_PARAMS; + +/** NV_ENC_ENCODE_OUT_PARAMS struct version. */ +#define NV_ENC_ENCODE_OUT_PARAMS_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * Creation parameters for input buffer. + */ +typedef struct _NV_ENC_CREATE_INPUT_BUFFER { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_CREATE_INPUT_BUFFER_VER */ + uint32_t width; /**< [in]: Input frame width */ + uint32_t height; /**< [in]: Input frame height */ + NV_ENC_MEMORY_HEAP memoryHeap; /**< [in]: Deprecated. Do not use */ + NV_ENC_BUFFER_FORMAT bufferFmt; /**< [in]: Input buffer format */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0 */ + NV_ENC_INPUT_PTR inputBuffer; /**< [out]: Pointer to input buffer */ + void* pSysMemBuffer; /**< [in]: Pointer to existing system memory buffer */ + uint32_t reserved1[57]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[63]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CREATE_INPUT_BUFFER; + +/** NV_ENC_CREATE_INPUT_BUFFER struct version. */ +#define NV_ENC_CREATE_INPUT_BUFFER_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * Creation parameters for output bitstream buffer. + */ +typedef struct _NV_ENC_CREATE_BITSTREAM_BUFFER { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_CREATE_BITSTREAM_BUFFER_VER */ + uint32_t size; /**< [in]: Deprecated. Do not use */ + NV_ENC_MEMORY_HEAP memoryHeap; /**< [in]: Deprecated. Do not use */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0 */ + NV_ENC_OUTPUT_PTR + bitstreamBuffer; /**< [out]: Pointer to the output bitstream buffer */ + void* bitstreamBufferPtr; /**< [out]: Reserved and should not be used */ + uint32_t reserved1[58]; /**< [in]: Reserved and should be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and should be set to NULL */ +} NV_ENC_CREATE_BITSTREAM_BUFFER; + +/** NV_ENC_CREATE_BITSTREAM_BUFFER struct version. */ +#define NV_ENC_CREATE_BITSTREAM_BUFFER_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * Structs needed for ME only mode. + */ +typedef struct _NV_ENC_MVECTOR { + int16_t mvx; /**< the x component of MV in quarter-pel units */ + int16_t mvy; /**< the y component of MV in quarter-pel units */ +} NV_ENC_MVECTOR; + +/** + * Motion vector structure per macroblock for H264 motion estimation. + */ +typedef struct _NV_ENC_H264_MV_DATA { + NV_ENC_MVECTOR mv[4]; /**< up to 4 vectors for 8x8 partition */ + uint8_t mbType; /**< 0 (I), 1 (P), 2 (IPCM), 3 (B) */ + uint8_t partitionType; /**< Specifies the block partition type. 0:16x16, + 1:8x8, 2:16x8, 3:8x16 */ + uint16_t reserved; /**< reserved padding for alignment */ + uint32_t mbCost; +} NV_ENC_H264_MV_DATA; + +/** + * Motion vector structure per CU for HEVC motion estimation. + */ +typedef struct _NV_ENC_HEVC_MV_DATA { + NV_ENC_MVECTOR mv[4]; /**< up to 4 vectors within a CU */ + uint8_t cuType; /**< 0 (I), 1(P) */ + uint8_t cuSize; /**< 0: 8x8, 1: 16x16, 2: 32x32, 3: 64x64 */ + uint8_t partitionMode; /**< The CU partition mode + 0 (2Nx2N), 1 (2NxN), 2(Nx2N), 3 (NxN), + 4 (2NxnU), 5 (2NxnD), 6(nLx2N), 7 (nRx2N) */ + uint8_t lastCUInCTB; /**< Marker to separate CUs in the current CTB from CUs + in the next CTB */ +} NV_ENC_HEVC_MV_DATA; + +/** + * Creation parameters for output motion vector buffer for ME only mode. + */ +typedef struct _NV_ENC_CREATE_MV_BUFFER { + uint32_t version; /**< [in]: Struct version. Must be set to + NV_ENC_CREATE_MV_BUFFER_VER */ + NV_ENC_OUTPUT_PTR + mvBuffer; /**< [out]: Pointer to the output motion vector buffer */ + uint32_t reserved1[255]; /**< [in]: Reserved and should be set to 0 */ + void* reserved2[63]; /**< [in]: Reserved and should be set to NULL */ +} NV_ENC_CREATE_MV_BUFFER; + +/** NV_ENC_CREATE_MV_BUFFER struct version*/ +#define NV_ENC_CREATE_MV_BUFFER_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * QP value for frames + */ +typedef struct _NV_ENC_QP { + uint32_t qpInterP; /**< [in]: Specifies QP value for P-frame. Even though this + field is uint32_t for legacy reasons, the client should + treat this as a signed parameter(int32_t) for cases in + which negative QP values are to be specified. */ + uint32_t qpInterB; /**< [in]: Specifies QP value for B-frame. Even though this + field is uint32_t for legacy reasons, the client should + treat this as a signed parameter(int32_t) for cases in + which negative QP values are to be specified. */ + uint32_t qpIntra; /**< [in]: Specifies QP value for Intra Frame. Even though + this field is uint32_t for legacy reasons, the client + should treat this as a signed parameter(int32_t) for + cases in which negative QP values are to be specified. */ +} NV_ENC_QP; + +/** + * Rate Control Configuration Parameters + */ +typedef struct _NV_ENC_RC_PARAMS { + uint32_t version; + NV_ENC_PARAMS_RC_MODE + rateControlMode; /**< [in]: Specifies the rate control mode. Check support + for various rate control modes using + ::NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES caps. */ + NV_ENC_QP constQP; /**< [in]: Specifies the initial QP to be used for + encoding, these values would be used for all frames if + in Constant QP mode. */ + uint32_t averageBitRate; /**< [in]: Specifies the average bitrate(in bits/sec) + used for encoding. */ + uint32_t + maxBitRate; /**< [in]: Specifies the maximum bitrate for the encoded + output. This is used for VBR and ignored for CBR mode. */ + uint32_t vbvBufferSize; /**< [in]: Specifies the VBV(HRD) buffer size. in + bits. Set 0 to use the default VBV buffer size. */ + uint32_t + vbvInitialDelay; /**< [in]: Specifies the VBV(HRD) initial delay in bits. + Set 0 to use the default VBV initial delay .*/ + uint32_t enableMinQP : 1; /**< [in]: Set this to 1 if minimum QP used for rate + control. */ + uint32_t enableMaxQP : 1; /**< [in]: Set this to 1 if maximum QP used for rate + control. */ + uint32_t enableInitialRCQP : 1; /**< [in]: Set this to 1 if user supplied + initial QP is used for rate control. */ + uint32_t enableAQ : 1; /**< [in]: Set this to 1 to enable adaptive + quantization (Spatial). */ + uint32_t reservedBitField1 : 1; /**< [in]: Reserved bitfields and must be set + to 0. */ + uint32_t enableLookahead : 1; /**< [in]: Set this to 1 to enable lookahead + with depth (if lookahead is + enabled, input frames must remain available + to the encoder until encode completion) */ + uint32_t disableIadapt : 1; /**< [in]: Set this to 1 to disable adaptive + I-frame insertion at scene cuts (only has an + effect when lookahead is enabled) */ + uint32_t disableBadapt : 1; /**< [in]: Set this to 1 to disable adaptive + B-frame decision (only has an effect when + lookahead is enabled) */ + uint32_t + enableTemporalAQ : 1; /**< [in]: Set this to 1 to enable temporal AQ */ + uint32_t zeroReorderDelay : 1; /**< [in]: Set this to 1 to indicate zero + latency operation (no reordering delay, + num_reorder_frames=0) */ + uint32_t enableNonRefP : 1; /**< [in]: Set this to 1 to enable automatic + insertion of non-reference P-frames (no effect + if enablePTD=0) */ + uint32_t strictGOPTarget : 1; /**< [in]: Set this to 1 to minimize GOP-to-GOP + rate fluctuations */ + uint32_t aqStrength : 4; /**< [in]: When AQ (Spatial) is enabled (i.e. + NV_ENC_RC_PARAMS::enableAQ is set), this field is + used to specify AQ strength. AQ strength scale is + from 1 (low) - 15 (aggressive). If not set, + strength is auto selected by driver. */ + uint32_t reservedBitFields : 16; /**< [in]: Reserved bitfields and must be set + to 0 */ + NV_ENC_QP minQP; /**< [in]: Specifies the minimum QP used for rate control. + Client must set NV_ENC_CONFIG::enableMinQP to 1. */ + NV_ENC_QP maxQP; /**< [in]: Specifies the maximum QP used for rate control. + Client must set NV_ENC_CONFIG::enableMaxQP to 1. */ + NV_ENC_QP + initialRCQP; /**< [in]: Specifies the initial QP used for rate control. + Client must set NV_ENC_CONFIG::enableInitialRCQP to 1. */ + uint32_t + temporallayerIdxMask; /**< [in]: Specifies the temporal layers (as a + bitmask) whose QPs have changed. Valid max + bitmask is [2^NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS + - 1]. Applicable only for constant QP mode + (NV_ENC_RC_PARAMS::rateControlMode = + NV_ENC_PARAMS_RC_CONSTQP). */ + uint8_t + temporalLayerQP[8]; /**< [in]: Specifies the temporal layer QPs used for + rate control. Temporal layer index is used as the + array index. Applicable only for constant QP mode + (NV_ENC_RC_PARAMS::rateControlMode = + NV_ENC_PARAMS_RC_CONSTQP). */ + uint8_t targetQuality; /**< [in]: Target CQ (Constant Quality) level for VBR + mode (range 0-51 with 0-automatic) */ + uint8_t targetQualityLSB; /**< [in]: Fractional part of target quality (as 8.8 + fixed point format) */ + uint16_t lookaheadDepth; /**< [in]: Maximum depth of lookahead with range + 0-(31 - number of B frames). lookaheadDepth is + only used if enableLookahead=1.*/ + uint8_t lowDelayKeyFrameScale; /**< [in]: Specifies the ratio of I frame bits + to P frame bits in case of single frame VBV + and CBR rate control mode, is set to 2 by + default for low latency tuning info and 1 by + default for ultra low latency tuning info */ + uint8_t reserved1[3]; + NV_ENC_QP_MAP_MODE + qpMapMode; /**< [in]: This flag is used to interpret values in array + specified by NV_ENC_PIC_PARAMS::qpDeltaMap. Set this to + NV_ENC_QP_MAP_EMPHASIS to treat values specified by + NV_ENC_PIC_PARAMS::qpDeltaMap as Emphasis Level Map. + Emphasis Level can be assigned any value specified + in enum NV_ENC_EMPHASIS_MAP_LEVEL. Emphasis Level Map is + used to specify regions to be encoded at varying levels of + quality. The hardware encoder adjusts the quantization + within the image as per the provided emphasis map, by + adjusting the quantization parameter (QP) assigned to each + macroblock. This adjustment is commonly called “Delta QP”. + The adjustment depends on the absolute QP decided by + the rate control algorithm, and is applied after the rate + control has decided each macroblock’s QP. Since the Delta QP + overrides rate control, enabling Emphasis Level Map may + violate bitrate and VBV buffer size constraints. Emphasis + Level Map is useful in situations where client has a priori + knowledge of the image complexity (e.g. via use of NVFBC's + Classification feature) and encoding those high-complexity + areas at higher quality (lower QP) is important, even at the + possible cost of violating bitrate/VBV buffer size + constraints This feature is not supported when AQ( + Spatial/Temporal) is enabled. This feature is only supported + for H264 codec currently. + + Set this to NV_ENC_QP_MAP_DELTA to treat values + specified by NV_ENC_PIC_PARAMS::qpDeltaMap as QP Delta. This + specifies QP modifier to be applied on top of the QP chosen + by rate control + + Set this to NV_ENC_QP_MAP_DISABLED to ignore + NV_ENC_PIC_PARAMS::qpDeltaMap values. In this case, + qpDeltaMap should be set to NULL. + + Other values are reserved for future use.*/ + NV_ENC_MULTI_PASS + multiPass; /**< [in]: This flag is used to enable multi-pass encoding for + a given ::NV_ENC_PARAMS_RC_MODE. This flag is not valid for + H264 and HEVC MEOnly mode */ + uint32_t + alphaLayerBitrateRatio; /**< [in]: Specifies the ratio in which bitrate + should be split between base and alpha layer. A + value 'x' for this field will split the target + bitrate in a ratio of x : 1 between base and + alpha layer. The default split ratio is 15.*/ + uint32_t reserved[5]; +} NV_ENC_RC_PARAMS; + +/** macro for constructing the version field of ::_NV_ENC_RC_PARAMS */ +#define NV_ENC_RC_PARAMS_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * \struct _NV_ENC_CONFIG_H264_VUI_PARAMETERS + * H264 Video Usability Info parameters + */ +typedef struct _NV_ENC_CONFIG_H264_VUI_PARAMETERS { + uint32_t overscanInfoPresentFlag; /**< [in]: if set to 1 , it specifies that + the overscanInfo is present */ + uint32_t overscanInfo; /**< [in]: Specifies the overscan info(as defined in + Annex E of the ITU-T Specification). */ + uint32_t videoSignalTypePresentFlag; /**< [in]: If set to 1, it specifies that + the videoFormat, videoFullRangeFlag + and colourDescriptionPresentFlag are + present. */ + uint32_t videoFormat; /**< [in]: Specifies the source video format(as defined + in Annex E of the ITU-T Specification).*/ + uint32_t videoFullRangeFlag; /**< [in]: Specifies the output range of the luma + and chroma samples(as defined in Annex E of + the ITU-T Specification). */ + uint32_t colourDescriptionPresentFlag; /**< [in]: If set to 1, it specifies + that the colourPrimaries, + transferCharacteristics and + colourMatrix are present. */ + uint32_t colourPrimaries; /**< [in]: Specifies color primaries for converting + to RGB(as defined in Annex E of the ITU-T + Specification) */ + uint32_t + transferCharacteristics; /**< [in]: Specifies the opto-electronic transfer + characteristics to use (as defined in Annex E + of the ITU-T Specification) */ + uint32_t + colourMatrix; /**< [in]: Specifies the matrix coefficients used in + deriving the luma and chroma from the RGB primaries (as + defined in Annex E of the ITU-T Specification). */ + uint32_t chromaSampleLocationFlag; /**< [in]: if set to 1 , it specifies that + the chromaSampleLocationTop and + chromaSampleLocationBot are present.*/ + uint32_t chromaSampleLocationTop; /**< [in]: Specifies the chroma sample + location for top field(as defined in + Annex E of the ITU-T Specification) */ + uint32_t chromaSampleLocationBot; /**< [in]: Specifies the chroma sample + location for bottom field(as defined in + Annex E of the ITU-T Specification) */ + uint32_t bitstreamRestrictionFlag; /**< [in]: if set to 1, it specifies the + bitstream restriction parameters are + present in the bitstream.*/ + uint32_t reserved[15]; +} NV_ENC_CONFIG_H264_VUI_PARAMETERS; + +typedef NV_ENC_CONFIG_H264_VUI_PARAMETERS NV_ENC_CONFIG_HEVC_VUI_PARAMETERS; + +/** + * \struct _NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE + * External motion vector hint counts per block type. + * H264 supports multiple hint while HEVC supports one hint for each valid + * candidate. + */ +typedef struct _NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE { + uint32_t + numCandsPerBlk16x16 : 4; /**< [in]: Supported for H264, HEVC. It Specifies + the number of candidates per 16x16 block. */ + uint32_t + numCandsPerBlk16x8 : 4; /**< [in]: Supported for H264 only. Specifies the + number of candidates per 16x8 block. */ + uint32_t + numCandsPerBlk8x16 : 4; /**< [in]: Supported for H264 only. Specifies the + number of candidates per 8x16 block. */ + uint32_t numCandsPerBlk8x8 : 4; /**< [in]: Supported for H264, HEVC. Specifies + the number of candidates per 8x8 block. */ + uint32_t reserved : 16; /**< [in]: Reserved for padding. */ + uint32_t reserved1[3]; /**< [in]: Reserved for future use. */ +} NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE; + +/** + * \struct _NVENC_EXTERNAL_ME_HINT + * External Motion Vector hint structure. + */ +typedef struct _NVENC_EXTERNAL_ME_HINT { + int32_t mvx : 12; /**< [in]: Specifies the x component of integer pixel MV + (relative to current MB) S12.0. */ + int32_t mvy : 10; /**< [in]: Specifies the y component of integer pixel MV + (relative to current MB) S10.0 .*/ + int32_t refidx : 5; /**< [in]: Specifies the reference index (31=invalid). + Current we support only 1 reference frame per direction + for external hints, so \p refidx must be 0. */ + int32_t dir : 1; /**< [in]: Specifies the direction of motion estimation . + 0=L0 1=L1.*/ + int32_t + partType : 2; /**< [in]: Specifies the block partition type.0=16x16 1=16x8 + 2=8x16 3=8x8 (blocks in partition must be consecutive).*/ + int32_t + lastofPart : 1; /**< [in]: Set to 1 for the last MV of (sub) partition */ + int32_t lastOfMB : 1; /**< [in]: Set to 1 for the last MV of macroblock. */ +} NVENC_EXTERNAL_ME_HINT; + +/** + * \struct _NV_ENC_CONFIG_H264 + * H264 encoder configuration parameters + */ +typedef struct _NV_ENC_CONFIG_H264 { + uint32_t enableTemporalSVC : 1; /**< [in]: Set to 1 to enable SVC temporal*/ + uint32_t enableStereoMVC : 1; /**< [in]: Set to 1 to enable stereo MVC*/ + uint32_t hierarchicalPFrames : 1; /**< [in]: Set to 1 to enable hierarchical P + Frames */ + uint32_t hierarchicalBFrames : 1; /**< [in]: Set to 1 to enable hierarchical B + Frames */ + uint32_t + outputBufferingPeriodSEI : 1; /**< [in]: Set to 1 to write SEI buffering + period syntax in the bitstream */ + uint32_t outputPictureTimingSEI : 1; /**< [in]: Set to 1 to write SEI picture + timing syntax in the bitstream. When + set for following rateControlMode : + NV_ENC_PARAMS_RC_CBR, + NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ, + NV_ENC_PARAMS_RC_CBR_HQ, + filler data is inserted if needed to + achieve HRD bitrate */ + uint32_t outputAUD : 1; /**< [in]: Set to 1 to write access unit delimiter + syntax in bitstream */ + uint32_t disableSPSPPS : 1; /**< [in]: Set to 1 to disable writing of Sequence + and Picture parameter info in bitstream */ + uint32_t outputFramePackingSEI : 1; /**< [in]: Set to 1 to enable writing of + frame packing arrangement SEI messages + to bitstream */ + uint32_t outputRecoveryPointSEI : 1; /**< [in]: Set to 1 to enable writing of + recovery point SEI message */ + uint32_t + enableIntraRefresh : 1; /**< [in]: Set to 1 to enable gradual decoder + refresh or intra refresh. If the GOP structure + uses B frames this will be ignored */ + uint32_t + enableConstrainedEncoding : 1; /**< [in]: Set this to 1 to enable + constrainedFrame encoding where each + slice in the constrained picture is + independent of other slices. Constrained + encoding works only with rectangular + slices. Check support for constrained + encoding using + ::NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING + caps. */ + uint32_t repeatSPSPPS : 1; /**< [in]: Set to 1 to enable writing of Sequence + and Picture parameter for every IDR frame */ + uint32_t enableVFR : 1; /**< [in]: Setting enableVFR=1 currently only sets the + fixed_frame_rate_flag=0 in the VUI but otherwise + has no impact on the encoder behavior. For + more details please refer to E.1 VUI syntax of + H.264 standard. Note, however, that NVENC does not + support VFR encoding and rate control. */ + uint32_t + enableLTR : 1; /**< [in]: Set to 1 to enable LTR (Long Term Reference) + frame support. LTR can be used in two modes: "LTR Trust" + mode and "LTR Per Picture" mode. LTR Trust mode: In this + mode, ltrNumFrames pictures after IDR are automatically + marked as LTR. This mode is enabled by setting + ltrTrustMode = 1. Use of LTR Trust mode is strongly + discouraged as this mode may be deprecated in future. + LTR Per Picture mode: In this mode, client can + control whether the current picture should be marked as + LTR. Enable this mode by setting ltrTrustMode = 0 and + ltrMarkFrame = 1 for the picture to be marked as LTR. + This is the preferred mode for using LTR. Note that LTRs + are not supported if encoding session is configured with + B-frames */ + uint32_t + qpPrimeYZeroTransformBypassFlag : 1; /**< [in]: To enable lossless + encode set this to 1, set QP + to 0 and RC_mode to + NV_ENC_PARAMS_RC_CONSTQP and + profile to + HIGH_444_PREDICTIVE_PROFILE. + Check support for + lossless encoding using + ::NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE + caps. */ + uint32_t useConstrainedIntraPred : 1; /**< [in]: Set 1 to enable constrained + intra prediction. */ + uint32_t + enableFillerDataInsertion : 1; /**< [in]: Set to 1 to enable insertion of + filler data in the bitstream. This flag + will take effect only when one of the + CBR rate control modes + (NV_ENC_PARAMS_RC_CBR, + NV_ENC_PARAMS_RC_CBR_HQ, + NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) + is in use and both + NV_ENC_INITIALIZE_PARAMS::frameRateNum + and + NV_ENC_INITIALIZE_PARAMS::frameRateDen + are set to non-zero values. Setting this + field when + NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem + is also set is currently not supported + and will make ::NvEncInitializeEncoder() + return an error. */ + uint32_t disableSVCPrefixNalu : 1; /**< [in]: Set to 1 to disable writing of + SVC Prefix NALU preceding each slice in + bitstream. Applicable only when temporal + SVC is enabled + (NV_ENC_CONFIG_H264::enableTemporalSVC = + 1). */ + uint32_t + enableScalabilityInfoSEI : 1; /**< [in]: Set to 1 to enable writing + of Scalability Information SEI + message preceding each IDR picture + in bitstream Applicable only when + temporal SVC is enabled + (NV_ENC_CONFIG_H264::enableTemporalSVC + = 1). */ + uint32_t reservedBitFields : 12; /**< [in]: Reserved bitfields and must be set + to 0 */ + uint32_t level; /**< [in]: Specifies the encoding level. Client is recommended + to set this to NV_ENC_LEVEL_AUTOSELECT in order to enable + the NvEncodeAPI interface to select the correct level. */ + uint32_t + idrPeriod; /**< [in]: Specifies the IDR interval. If not set, this is made + equal to gopLength in NV_ENC_CONFIG.Low latency application + client can set IDR interval to NVENC_INFINITE_GOPLENGTH so + that IDR frames are not inserted automatically. */ + uint32_t separateColourPlaneFlag; /**< [in]: Set to 1 to enable 4:4:4 separate + colour planes */ + uint32_t + disableDeblockingFilterIDC; /**< [in]: Specifies the deblocking filter + mode. Permissible value range: [0,2]. This + flag corresponds to the flag + disable_deblocking_filter_idc specified in + section 7.4.3 of H.264 specification, which + specifies whether the operation of the + deblocking filter shall be disabled across + some block edges of the slice and specifies + for which edges the filtering is disabled. + See section 7.4.3 of H.264 specification + for more details.*/ + uint32_t numTemporalLayers; /**< [in]: Specifies number of temporal layers to + be used for hierarchical coding / temporal SVC. + Valid value range is + [1,::NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS] */ + uint32_t spsId; /**< [in]: Specifies the SPS id of the sequence header */ + uint32_t ppsId; /**< [in]: Specifies the PPS id of the picture header */ + NV_ENC_H264_ADAPTIVE_TRANSFORM_MODE + adaptiveTransformMode; /**< [in]: Specifies the AdaptiveTransform Mode. + Check support for AdaptiveTransform mode using + ::NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM caps. + */ + NV_ENC_H264_FMO_MODE + fmoMode; /**< [in]: Specified the FMO Mode. Check support for FMO using + ::NV_ENC_CAPS_SUPPORT_FMO caps. */ + NV_ENC_H264_BDIRECT_MODE + bdirectMode; /**< [in]: Specifies the BDirect mode. Check support for + BDirect mode using ::NV_ENC_CAPS_SUPPORT_BDIRECT_MODE + caps.*/ + NV_ENC_H264_ENTROPY_CODING_MODE + entropyCodingMode; /**< [in]: Specifies the entropy coding mode. Check + support for CABAC mode using + ::NV_ENC_CAPS_SUPPORT_CABAC caps. */ + NV_ENC_STEREO_PACKING_MODE + stereoMode; /**< [in]: Specifies the stereo frame packing mode which is to + be signaled in frame packing arrangement SEI */ + uint32_t intraRefreshPeriod; /**< [in]: Specifies the interval between + successive intra refresh if enableIntrarefresh + is set. Requires enableIntraRefresh to be set. + Will be disabled if NV_ENC_CONFIG::gopLength + is not set to NVENC_INFINITE_GOPLENGTH. */ + uint32_t + intraRefreshCnt; /**< [in]: Specifies the length of intra refresh in + number of frames for periodic intra refresh. This + value should be smaller than intraRefreshPeriod */ + uint32_t + maxNumRefFrames; /**< [in]: Specifies the DPB size used for encoding. + Setting it to 0 will let driver use the default DPB + size. The low latency application which wants to + invalidate reference frame as an error resilience tool + is recommended to use a large DPB size so that + the encoder can keep old reference frames which can be + used if recent frames are invalidated. */ + uint32_t + sliceMode; /**< [in]: This parameter in conjunction with sliceModeData + specifies the way in which the picture is divided into + slices sliceMode = 0 MB based slices, sliceMode = 1 Byte + based slices, sliceMode = 2 MB row based slices, sliceMode = + 3 numSlices in Picture. When forceIntraRefreshWithFrameCnt + is set it will have priority over sliceMode setting When + sliceMode == 0 and sliceModeData == 0 whole picture will be + coded with one slice */ + uint32_t + sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. + For: sliceMode = 0, sliceModeData specifies # of MBs in + each slice (except last slice) sliceMode = 1, + sliceModeData specifies maximum # of bytes in each slice + (except last slice) sliceMode = 2, sliceModeData + specifies # of MB rows in each slice (except last slice) + sliceMode = 3, sliceModeData specifies number of + slices in the picture. Driver will divide picture into + slices optimally */ + NV_ENC_CONFIG_H264_VUI_PARAMETERS + h264VUIParameters; /**< [in]: Specifies the H264 video usability info + parameters */ + uint32_t + ltrNumFrames; /**< [in]: Specifies the number of LTR frames. This + parameter has different meaning in two LTR modes. In "LTR + Trust" mode (ltrTrustMode = 1), encoder will mark the + first ltrNumFrames base layer reference frames within + each IDR interval as LTR. In "LTR Per Picture" mode + (ltrTrustMode = 0 and ltrMarkFrame = 1), ltrNumFrames + specifies maximum number of LTR frames in DPB. */ + uint32_t + ltrTrustMode; /**< [in]: Specifies the LTR operating mode. See + comments near NV_ENC_CONFIG_H264::enableLTR for + description of the two modes. Set to 1 to use "LTR + Trust" mode of LTR operation. Clients are + discouraged to use "LTR Trust" mode as this mode may + be deprecated in future releases. Set to 0 when + using "LTR Per Picture" mode of LTR operation. */ + uint32_t chromaFormatIDC; /**< [in]: Specifies the chroma format. Should be + set to 1 for yuv420 input, 3 for yuv444 input. + Check support for YUV444 encoding using + ::NV_ENC_CAPS_SUPPORT_YUV444_ENCODE caps.*/ + uint32_t + maxTemporalLayers; /**< [in]: Specifies the maximum temporal layer used + for temporal SVC / hierarchical coding. Defaut value + of this field is + NV_ENC_CAPS::NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS. + Note that the value + NV_ENC_CONFIG_H264::maxNumRefFrames should be + greater than or equal to + (NV_ENC_CONFIG_H264::maxTemporalLayers - 2) * 2, for + NV_ENC_CONFIG_H264::maxTemporalLayers >= 2.*/ + NV_ENC_BFRAME_REF_MODE + useBFramesAsRef; /**< [in]: Specifies the B-Frame as reference mode. Check + support for useBFramesAsRef mode using + ::NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE caps.*/ + NV_ENC_NUM_REF_FRAMES + numRefL0; /**< [in]: Specifies max number of reference frames in reference + picture list L0, that can be used by hardware for prediction + of a frame. Check support for numRefL0 using + ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */ + NV_ENC_NUM_REF_FRAMES + numRefL1; /**< [in]: Specifies max number of reference frames in reference + picture list L1, that can be used by hardware for prediction + of a frame. Check support for numRefL1 using + ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */ + uint32_t reserved1[267]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CONFIG_H264; + +/** + * \struct _NV_ENC_CONFIG_HEVC + * HEVC encoder configuration parameters to be set during initialization. + */ +typedef struct _NV_ENC_CONFIG_HEVC { + uint32_t level; /**< [in]: Specifies the level of the encoded bitstream.*/ + uint32_t tier; /**< [in]: Specifies the level tier of the encoded bitstream.*/ + NV_ENC_HEVC_CUSIZE + minCUSize; /**< [in]: Specifies the minimum size of luma coding unit.*/ + NV_ENC_HEVC_CUSIZE + maxCUSize; /**< [in]: Specifies the maximum size of luma coding unit. + Currently NVENC SDK only supports maxCUSize equal to + NV_ENC_HEVC_CUSIZE_32x32.*/ + uint32_t useConstrainedIntraPred : 1; /**< [in]: Set 1 to enable constrained + intra prediction. */ + uint32_t disableDeblockAcrossSliceBoundary : 1; /**< [in]: Set 1 to disable in + loop filtering across slice + boundary.*/ + uint32_t outputBufferingPeriodSEI : 1; /**< [in]: Set 1 to write SEI buffering + period syntax in the bitstream */ + uint32_t outputPictureTimingSEI : 1; /**< [in]: Set 1 to write SEI picture + timing syntax in the bitstream */ + uint32_t + outputAUD : 1; /**< [in]: Set 1 to write Access Unit Delimiter syntax. */ + uint32_t + enableLTR : 1; /**< [in]: Set to 1 to enable LTR (Long Term Reference) + frame support. LTR can be used in two modes: "LTR Trust" + mode and "LTR Per Picture" mode. LTR Trust mode: In this + mode, ltrNumFrames pictures after IDR are automatically + marked as LTR. This mode is enabled by setting + ltrTrustMode = 1. Use of LTR Trust mode is strongly + discouraged as this mode may be deprecated in future + releases. LTR Per Picture mode: In this mode, client can + control whether the current picture should be marked as + LTR. Enable this mode by setting ltrTrustMode = 0 and + ltrMarkFrame = 1 for the picture to be marked as LTR. + This is the preferred mode for using LTR. Note that LTRs + are not supported if encoding session is configured with + B-frames */ + uint32_t disableSPSPPS : 1; /**< [in]: Set 1 to disable VPS, SPS and PPS + signaling in the bitstream. */ + uint32_t repeatSPSPPS : 1; /**< [in]: Set 1 to output VPS,SPS and PPS for + every IDR frame.*/ + uint32_t + enableIntraRefresh : 1; /**< [in]: Set 1 to enable gradual decoder refresh + or intra refresh. If the GOP structure uses B + frames this will be ignored */ + uint32_t + chromaFormatIDC : 2; /**< [in]: Specifies the chroma format. Should be set + to 1 for yuv420 input, 3 for yuv444 input.*/ + uint32_t pixelBitDepthMinus8 : 3; /**< [in]: Specifies pixel bit depth + minus 8. Should be set to 0 for 8 bit + input, 2 for 10 bit input.*/ + uint32_t + enableFillerDataInsertion : 1; /**< [in]: Set to 1 to enable insertion of + filler data in the bitstream. This flag + will take effect only when one of the + CBR rate control modes + (NV_ENC_PARAMS_RC_CBR, + NV_ENC_PARAMS_RC_CBR_HQ, + NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) + is in use and both + NV_ENC_INITIALIZE_PARAMS::frameRateNum + and + NV_ENC_INITIALIZE_PARAMS::frameRateDen + are set to non-zero values. Setting this + field when + NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem + is also set is currently not supported + and will make ::NvEncInitializeEncoder() + return an error. */ + uint32_t + enableConstrainedEncoding : 1; /**< [in]: Set this to 1 to enable + constrainedFrame encoding where each + slice in the constrained picture is + independent of other slices. Constrained + encoding works only with rectangular + slices. Check support for + constrained encoding using + ::NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING + caps. */ + uint32_t enableAlphaLayerEncoding : 1; /**< [in]: Set this to 1 to enable HEVC + encode with alpha layer. */ + uint32_t reserved : 15; /**< [in]: Reserved bitfields.*/ + uint32_t + idrPeriod; /**< [in]: Specifies the IDR interval. If not set, this is made + equal to gopLength in NV_ENC_CONFIG. Low latency application + client can set IDR interval to NVENC_INFINITE_GOPLENGTH so + that IDR frames are not inserted automatically. */ + uint32_t intraRefreshPeriod; /**< [in]: Specifies the interval between + successive intra refresh if enableIntrarefresh is + set. Requires enableIntraRefresh to be set. Will + be disabled if NV_ENC_CONFIG::gopLength is not + set to NVENC_INFINITE_GOPLENGTH. */ + uint32_t + intraRefreshCnt; /**< [in]: Specifies the length of intra refresh in + number of frames for periodic intra refresh. This + value should be smaller than intraRefreshPeriod */ + uint32_t maxNumRefFramesInDPB; /**< [in]: Specifies the maximum number of + references frames in the DPB.*/ + uint32_t ltrNumFrames; /**< [in]: This parameter has different meaning in two + LTR modes. In "LTR Trust" mode (ltrTrustMode = 1), + encoder will mark the first ltrNumFrames base layer + reference frames within each IDR interval as LTR. In + "LTR Per Picture" mode (ltrTrustMode = 0 and + ltrMarkFrame = 1), ltrNumFrames specifies maximum + number of LTR frames in DPB. */ + uint32_t vpsId; /**< [in]: Specifies the VPS id of the video parameter set */ + uint32_t spsId; /**< [in]: Specifies the SPS id of the sequence header */ + uint32_t ppsId; /**< [in]: Specifies the PPS id of the picture header */ + uint32_t sliceMode; /**< [in]: This parameter in conjunction with + sliceModeData specifies the way in which the picture is + divided into slices sliceMode = 0 CTU based slices, + sliceMode = 1 Byte based slices, sliceMode = 2 CTU row + based slices, sliceMode = 3, numSlices in Picture When + sliceMode == 0 and sliceModeData == 0 whole picture + will be coded with one slice */ + uint32_t + sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. + For: sliceMode = 0, sliceModeData specifies # of CTUs in + each slice (except last slice) sliceMode = 1, + sliceModeData specifies maximum # of bytes in each slice + (except last slice) sliceMode = 2, sliceModeData + specifies # of CTU rows in each slice (except last + slice) sliceMode = 3, sliceModeData specifies number of + slices in the picture. Driver will divide picture into + slices optimally */ + uint32_t maxTemporalLayersMinus1; /**< [in]: Specifies the max temporal layer + used for hierarchical coding. */ + NV_ENC_CONFIG_HEVC_VUI_PARAMETERS + hevcVUIParameters; /**< [in]: Specifies the HEVC video usability info + parameters */ + uint32_t ltrTrustMode; /**< [in]: Specifies the LTR operating mode. See + comments near NV_ENC_CONFIG_HEVC::enableLTR for + description of the two modes. Set to 1 to use "LTR + Trust" mode of LTR operation. Clients are + discouraged to use "LTR Trust" mode as this mode may + be deprecated in future releases. Set to 0 when + using "LTR Per Picture" mode of LTR operation. */ + NV_ENC_BFRAME_REF_MODE + useBFramesAsRef; /**< [in]: Specifies the B-Frame as reference mode. Check + support for useBFramesAsRef mode using + ::NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE caps.*/ + NV_ENC_NUM_REF_FRAMES + numRefL0; /**< [in]: Specifies max number of reference frames in reference + picture list L0, that can be used by hardware for prediction + of a frame. Check support for numRefL0 using + ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */ + NV_ENC_NUM_REF_FRAMES + numRefL1; /**< [in]: Specifies max number of reference frames in reference + picture list L1, that can be used by hardware for prediction + of a frame. Check support for numRefL1 using + ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */ + uint32_t reserved1[214]; /**< [in]: Reserved and must be set to 0.*/ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CONFIG_HEVC; + +/** + * \struct _NV_ENC_CONFIG_H264_MEONLY + * H264 encoder configuration parameters for ME only Mode + * + */ +typedef struct _NV_ENC_CONFIG_H264_MEONLY { + uint32_t disablePartition16x16 : 1; /**< [in]: Disable Motion Estimation on + 16x16 blocks*/ + uint32_t disablePartition8x16 : 1; /**< [in]: Disable Motion Estimation on + 8x16 blocks*/ + uint32_t disablePartition16x8 : 1; /**< [in]: Disable Motion Estimation on + 16x8 blocks*/ + uint32_t disablePartition8x8 : 1; /**< [in]: Disable Motion Estimation on 8x8 + blocks*/ + uint32_t disableIntraSearch : 1; /**< [in]: Disable Intra search during Motion + Estimation*/ + uint32_t + bStereoEnable : 1; /**< [in]: Enable Stereo Mode for Motion Estimation + where each view is independently executed*/ + uint32_t reserved : 26; /**< [in]: Reserved and must be set to 0 */ + uint32_t reserved1[255]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CONFIG_H264_MEONLY; + +/** + * \struct _NV_ENC_CONFIG_HEVC_MEONLY + * HEVC encoder configuration parameters for ME only Mode + * + */ +typedef struct _NV_ENC_CONFIG_HEVC_MEONLY { + uint32_t reserved[256]; /**< [in]: Reserved and must be set to 0 */ + void* reserved1[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CONFIG_HEVC_MEONLY; + +/** + * \struct _NV_ENC_CODEC_CONFIG + * Codec-specific encoder configuration parameters to be set during + * initialization. + */ +typedef union _NV_ENC_CODEC_CONFIG { + NV_ENC_CONFIG_H264 h264Config; /**< [in]: Specifies the H.264-specific encoder + configuration. */ + NV_ENC_CONFIG_HEVC hevcConfig; /**< [in]: Specifies the HEVC-specific encoder + configuration. */ + NV_ENC_CONFIG_H264_MEONLY + h264MeOnlyConfig; /**< [in]: Specifies the H.264-specific ME only encoder + configuration. */ + NV_ENC_CONFIG_HEVC_MEONLY + hevcMeOnlyConfig; /**< [in]: Specifies the HEVC-specific ME only encoder + configuration. */ + uint32_t reserved[320]; /**< [in]: Reserved and must be set to 0 */ +} NV_ENC_CODEC_CONFIG; + +/** + * \struct _NV_ENC_CONFIG + * Encoder configuration parameters to be set during initialization. + */ +typedef struct _NV_ENC_CONFIG { + uint32_t + version; /**< [in]: Struct version. Must be set to ::NV_ENC_CONFIG_VER. */ + GUID profileGUID; /**< [in]: Specifies the codec profile GUID. If client + specifies \p NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID the + NvEncodeAPI interface will select the appropriate codec + profile. */ + uint32_t gopLength; /**< [in]: Specifies the number of pictures in one GOP. + Low latency application client can set goplength to + NVENC_INFINITE_GOPLENGTH so that keyframes are not + inserted automatically. */ + int32_t frameIntervalP; /**< [in]: Specifies the GOP pattern as follows: \p + frameIntervalP = 0: I, 1: IPP, 2: IBP, 3: IBBP If + goplength is set to NVENC_INFINITE_GOPLENGTH \p + frameIntervalP should be set to 1. */ + uint32_t monoChromeEncoding; /**< [in]: Set this to 1 to enable monochrome + encoding for this session. */ + NV_ENC_PARAMS_FRAME_FIELD_MODE + frameFieldMode; /**< [in]: Specifies the frame/field mode. + Check support for field encoding using + ::NV_ENC_CAPS_SUPPORT_FIELD_ENCODING caps. Using a + frameFieldMode other than + NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME for RGB input is + not supported. */ + NV_ENC_MV_PRECISION mvPrecision; /**< [in]: Specifies the desired motion + vector prediction precision. */ + NV_ENC_RC_PARAMS rcParams; /**< [in]: Specifies the rate control parameters + for the current encoding session. */ + NV_ENC_CODEC_CONFIG + encodeCodecConfig; /**< [in]: Specifies the codec specific config + parameters through this union. */ + uint32_t reserved[278]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CONFIG; + +/** macro for constructing the version field of ::_NV_ENC_CONFIG */ +#define NV_ENC_CONFIG_VER (NVENCAPI_STRUCT_VERSION(7) | (1 << 31)) + +/** + * Tuning information of NVENC encoding (TuningInfo is not applicable to H264 + * and HEVC MEOnly mode). + */ +typedef enum NV_ENC_TUNING_INFO { + NV_ENC_TUNING_INFO_UNDEFINED = + 0, /**< Undefined tuningInfo. Invalid value for encoding. */ + NV_ENC_TUNING_INFO_HIGH_QUALITY = + 1, /**< Tune presets for latency tolerant encoding.*/ + NV_ENC_TUNING_INFO_LOW_LATENCY = + 2, /**< Tune presets for low latency streaming.*/ + NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY = + 3, /**< Tune presets for ultra low latency streaming.*/ + NV_ENC_TUNING_INFO_LOSSLESS = 4, /**< Tune presets for lossless encoding.*/ + NV_ENC_TUNING_INFO_COUNT /**< Count number of tuningInfos. Invalid value. */ +} NV_ENC_TUNING_INFO; + +/** + * \struct _NV_ENC_INITIALIZE_PARAMS + * Encode Session Initialization parameters. + */ +typedef struct _NV_ENC_INITIALIZE_PARAMS { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_INITIALIZE_PARAMS_VER. */ + GUID encodeGUID; /**< [in]: Specifies the Encode GUID for which the encoder is + being created. ::NvEncInitializeEncoder() API will fail if + this is not set, or set to unsupported value. */ + GUID presetGUID; /**< [in]: Specifies the preset for encoding. If the preset + GUID is set then , the preset configuration will be + applied before any other parameter. */ + uint32_t encodeWidth; /**< [in]: Specifies the encode width. If not set + ::NvEncInitializeEncoder() API will fail. */ + uint32_t encodeHeight; /**< [in]: Specifies the encode height. If not set + ::NvEncInitializeEncoder() API will fail. */ + uint32_t darWidth; /**< [in]: Specifies the display aspect ratio Width. */ + uint32_t darHeight; /**< [in]: Specifies the display aspect ratio height. */ + uint32_t frameRateNum; /**< [in]: Specifies the numerator for frame rate used + for encoding in frames per second ( Frame rate = + frameRateNum / frameRateDen ). */ + uint32_t frameRateDen; /**< [in]: Specifies the denominator for frame rate + used for encoding in frames per second ( Frame rate + = frameRateNum / frameRateDen ). */ + uint32_t enableEncodeAsync; /**< [in]: Set this to 1 to enable asynchronous + mode and is expected to use events to get + picture completion notification. */ + uint32_t enablePTD; /**< [in]: Set this to 1 to enable the Picture Type + Decision is be taken by the NvEncodeAPI interface. */ + uint32_t + reportSliceOffsets : 1; /**< [in]: Set this to 1 to enable reporting + slice offsets in ::_NV_ENC_LOCK_BITSTREAM. + NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync + must be set to 0 to use this feature. + Client must set this to 0 if + NV_ENC_CONFIG_H264::sliceMode is 1 on + Kepler GPUs */ + uint32_t + enableSubFrameWrite : 1; /**< [in]: Set this to 1 to write out available + bitstream to memory at subframe intervals. If + enableSubFrameWrite = 1, then the hardware + encoder returns data as soon as a slice has + completed encoding. This results in better + encoding latency, but the downside is that the + application has to keep polling via a call to + nvEncLockBitstream API continuously to see if + any encoded slice data is available. Use this + mode if you feel that the marginal reduction + in latency from sub-frame encoding is worth + the increase in complexity due to CPU-based + polling. */ + uint32_t + enableExternalMEHints : 1; /**< [in]: Set to 1 to enable external ME + hints for the current frame. For + NV_ENC_INITIALIZE_PARAMS::enablePTD=1 + with B frames, programming L1 hints is + optional for B frames since Client + doesn't know internal GOP structure. + NV_ENC_PIC_PARAMS::meHintRefPicDist + should preferably be set with + enablePTD=1. */ + uint32_t enableMEOnlyMode : 1; /**< [in]: Set to 1 to enable ME Only Mode .*/ + uint32_t + enableWeightedPrediction : 1; /**< [in]: Set this to 1 to enable weighted + prediction. Not supported if encode + session is configured for B-Frames (i.e. + NV_ENC_CONFIG::frameIntervalP > 1 or + preset >=P3 when tuningInfo = + ::NV_ENC_TUNING_INFO_HIGH_QUALITY or + tuningInfo = + ::NV_ENC_TUNING_INFO_LOSSLESS. This is + because preset >=p3 internally enables B + frames when tuningInfo = + ::NV_ENC_TUNING_INFO_HIGH_QUALITY or + ::NV_ENC_TUNING_INFO_LOSSLESS). */ + uint32_t enableOutputInVidmem : 1; /**< [in]: Set this to 1 to enable output + of NVENC in video memory buffer created + by application. This feature is not + supported for HEVC ME only mode. */ + uint32_t reservedBitFields : 26; /**< [in]: Reserved bitfields and must be set + to 0 */ + uint32_t privDataSize; /**< [in]: Reserved private data buffer size and must + be set to 0 */ + void* privData; /**< [in]: Reserved private data buffer and must be set to + NULL */ + NV_ENC_CONFIG* + encodeConfig; /**< [in]: Specifies the advanced codec specific structure. + If client has sent a valid codec config structure, it + will override parameters set by the + NV_ENC_INITIALIZE_PARAMS::presetGUID parameter. If set to + NULL the NvEncodeAPI interface will use the + NV_ENC_INITIALIZE_PARAMS::presetGUID to set the codec + specific parameters. Client can also optionally query the + NvEncodeAPI interface to get codec specific parameters + for a presetGUID using ::NvEncGetEncodePresetConfig() + API. It can then modify (if required) some of the codec + config parameters and send down a custom config structure + as part of ::_NV_ENC_INITIALIZE_PARAMS. Even in this case + client is recommended to pass the same preset guid it has + used in ::NvEncGetEncodePresetConfig() API to query the + config structure; as + NV_ENC_INITIALIZE_PARAMS::presetGUID. This will not + override the custom config structure but will be used to + determine other Encoder HW specific parameters not + exposed in the API. */ + uint32_t maxEncodeWidth; /**< [in]: Maximum encode width to be used for + current Encode session. Client should allocate + output buffers according to this dimension for + dynamic resolution change. If set to 0, Encoder + will not allow dynamic resolution change. */ + uint32_t maxEncodeHeight; /**< [in]: Maximum encode height to be allowed for + current Encode session. Client should allocate + output buffers according to this dimension for + dynamic resolution change. If set to 0, Encode + will not allow dynamic resolution change. */ + NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE maxMEHintCountsPerBlock + [2]; /**< [in]: If Client wants to pass external motion vectors in + NV_ENC_PIC_PARAMS::meExternalHints buffer it must specify the + maximum number of hint candidates per block per direction for the + encode session. The + NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[0] is for L0 + predictors and + NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[1] is for L1 + predictors. This client must also set + NV_ENC_INITIALIZE_PARAMS::enableExternalMEHints to 1. */ + NV_ENC_TUNING_INFO + tuningInfo; /**< [in]: Tuning Info of NVENC encoding(TuningInfo is not + applicable to H264 and HEVC meonly mode). */ + uint32_t reserved[288]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_INITIALIZE_PARAMS; + +/** macro for constructing the version field of ::_NV_ENC_INITIALIZE_PARAMS */ +#define NV_ENC_INITIALIZE_PARAMS_VER (NVENCAPI_STRUCT_VERSION(5) | (1 << 31)) + +/** + * \struct _NV_ENC_RECONFIGURE_PARAMS + * Encode Session Reconfigured parameters. + */ +typedef struct _NV_ENC_RECONFIGURE_PARAMS { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_RECONFIGURE_PARAMS_VER. */ + NV_ENC_INITIALIZE_PARAMS + reInitEncodeParams; /**< [in]: Encoder session re-initialization + parameters. If reInitEncodeParams.encodeConfig is + NULL and reInitEncodeParams.presetGUID is the same + as the preset GUID specified on the call to + NvEncInitializeEncoder(), EncodeAPI will continue + to use the existing encode configuration. If + reInitEncodeParams.encodeConfig is NULL and + reInitEncodeParams.presetGUID is different + from the preset GUID specified on the call to + NvEncInitializeEncoder(), EncodeAPI will try to use + the default configuration for the preset specified + by reInitEncodeParams.presetGUID. In this case, + reconfiguration may fail if the new configuration + is incompatible with the existing configuration + (e.g. the new configuration results in a change in + the GOP structure). */ + uint32_t resetEncoder : 1; /**< [in]: This resets the rate control states and + other internal encoder states. This should be + used only with an IDR frame. If + NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1, + encoder will force the frame type to IDR */ + uint32_t + forceIDR : 1; /**< [in]: Encode the current picture as an IDR picture. + This flag is only valid when Picture type decision is + taken by the Encoder + [_NV_ENC_INITIALIZE_PARAMS::enablePTD == 1]. */ + uint32_t reserved : 30; + +} NV_ENC_RECONFIGURE_PARAMS; + +/** macro for constructing the version field of ::_NV_ENC_RECONFIGURE_PARAMS */ +#define NV_ENC_RECONFIGURE_PARAMS_VER (NVENCAPI_STRUCT_VERSION(1) | (1 << 31)) + +/** + * \struct _NV_ENC_PRESET_CONFIG + * Encoder preset config + */ +typedef struct _NV_ENC_PRESET_CONFIG { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_PRESET_CONFIG_VER. */ + NV_ENC_CONFIG presetCfg; /**< [out]: preset config returned by the Nvidia + Video Encoder interface. */ + uint32_t reserved1[255]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_PRESET_CONFIG; + +/** macro for constructing the version field of ::_NV_ENC_PRESET_CONFIG */ +#define NV_ENC_PRESET_CONFIG_VER (NVENCAPI_STRUCT_VERSION(4) | (1 << 31)) + +/** + * \struct _NV_ENC_PIC_PARAMS_MVC + * MVC-specific parameters to be sent on a per-frame basis. + */ +typedef struct _NV_ENC_PIC_PARAMS_MVC { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_PIC_PARAMS_MVC_VER. */ + uint32_t viewID; /**< [in]: Specifies the view ID associated with the current + input view. */ + uint32_t temporalID; /**< [in]: Specifies the temporal ID associated with the + current input view. */ + uint32_t priorityID; /**< [in]: Specifies the priority ID associated with the + current input view. Reserved and ignored by the + NvEncodeAPI interface. */ + uint32_t reserved1[12]; /**< [in]: Reserved and must be set to 0. */ + void* reserved2[8]; /**< [in]: Reserved and must be set to NULL. */ +} NV_ENC_PIC_PARAMS_MVC; + +/** macro for constructing the version field of ::_NV_ENC_PIC_PARAMS_MVC */ +#define NV_ENC_PIC_PARAMS_MVC_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * \union _NV_ENC_PIC_PARAMS_H264_EXT + * H264 extension picture parameters + */ +typedef union _NV_ENC_PIC_PARAMS_H264_EXT { + NV_ENC_PIC_PARAMS_MVC + mvcPicParams; /**< [in]: Specifies the MVC picture parameters. */ + uint32_t reserved1[32]; /**< [in]: Reserved and must be set to 0. */ +} NV_ENC_PIC_PARAMS_H264_EXT; + +/** + * \struct _NV_ENC_SEI_PAYLOAD + * User SEI message + */ +typedef struct _NV_ENC_SEI_PAYLOAD { + uint32_t payloadSize; /**< [in] SEI payload size in bytes. SEI payload must be + byte aligned, as described in Annex D */ + uint32_t payloadType; /**< [in] SEI payload types and syntax can be found in + Annex D of the H.264 Specification. */ + uint8_t* payload; /**< [in] pointer to user data */ +} NV_ENC_SEI_PAYLOAD; + +#define NV_ENC_H264_SEI_PAYLOAD NV_ENC_SEI_PAYLOAD + +/** + * \struct _NV_ENC_PIC_PARAMS_H264 + * H264 specific enc pic params. sent on a per frame basis. + */ +typedef struct _NV_ENC_PIC_PARAMS_H264 { + uint32_t displayPOCSyntax; /**< [in]: Specifies the display POC syntax This is + required to be set if client is handling the + picture type decision. */ + uint32_t reserved3; /**< [in]: Reserved and must be set to 0 */ + uint32_t + refPicFlag; /**< [in]: Set to 1 for a reference picture. This is ignored + if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */ + uint32_t colourPlaneId; /**< [in]: Specifies the colour plane ID associated + with the current input. */ + uint32_t + forceIntraRefreshWithFrameCnt; /**< [in]: Forces an intra refresh with + duration equal to intraRefreshFrameCnt. + When outputRecoveryPointSEI is + set this is value is used for + recovery_frame_cnt in recovery point SEI + message forceIntraRefreshWithFrameCnt + cannot be used if B frames are used in + the GOP structure specified */ + uint32_t + constrainedFrame : 1; /**< [in]: Set to 1 if client wants to encode + this frame with each slice completely + independent of other slices in the frame. + NV_ENC_INITIALIZE_PARAMS::enableConstrainedEncoding + should be set to 1 */ + uint32_t + sliceModeDataUpdate : 1; /**< [in]: Set to 1 if client wants to change the + sliceModeData field to specify new sliceSize + Parameter When forceIntraRefreshWithFrameCnt + is set it will have priority over sliceMode + setting */ + uint32_t ltrMarkFrame : 1; /**< [in]: Set to 1 if client wants to mark this + frame as LTR */ + uint32_t + ltrUseFrames : 1; /**< [in]: Set to 1 if client allows encoding this frame + using the LTR frames specified in ltrFrameBitmap */ + uint32_t reservedBitFields : 28; /**< [in]: Reserved bit fields and must be + set to 0 */ + uint8_t* sliceTypeData; /**< [in]: Deprecated. */ + uint32_t sliceTypeArrayCnt; /**< [in]: Deprecated. */ + uint32_t seiPayloadArrayCnt; /**< [in]: Specifies the number of elements + allocated in seiPayloadArray array. */ + NV_ENC_SEI_PAYLOAD* seiPayloadArray; /**< [in]: Array of SEI payloads which + will be inserted for this frame. */ + uint32_t + sliceMode; /**< [in]: This parameter in conjunction with sliceModeData + specifies the way in which the picture is divided into + slices sliceMode = 0 MB based slices, sliceMode = 1 Byte + based slices, sliceMode = 2 MB row based slices, sliceMode = + 3, numSlices in Picture When forceIntraRefreshWithFrameCnt + is set it will have priority over sliceMode setting When + sliceMode == 0 and sliceModeData == 0 whole picture will be + coded with one slice */ + uint32_t + sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. + For: sliceMode = 0, sliceModeData specifies # of MBs in + each slice (except last slice) sliceMode = 1, + sliceModeData specifies maximum # of bytes in each slice + (except last slice) sliceMode = 2, sliceModeData + specifies # of MB rows in each slice (except last slice) + sliceMode = 3, sliceModeData specifies number of + slices in the picture. Driver will divide picture into + slices optimally */ + uint32_t ltrMarkFrameIdx; /**< [in]: Specifies the long term referenceframe + index to use for marking this frame as LTR.*/ + uint32_t + ltrUseFrameBitmap; /**< [in]: Specifies the associated bitmap of LTR frame + indices to use when encoding this frame. */ + uint32_t ltrUsageMode; /**< [in]: Not supported. Reserved for future use and + must be set to 0. */ + uint32_t forceIntraSliceCount; /**< [in]: Specifies the number of slices to be + forced to Intra in the current picture. This + option along with forceIntraSliceIdx[] array + needs to be used with sliceMode = 3 only */ + uint32_t* + forceIntraSliceIdx; /**< [in]: Slice indices to be forced to intra in the + current picture. Each slice index should be <= + num_slices_in_picture -1. Index starts from 0 for + first slice. The number of entries in this array + should be equal to forceIntraSliceCount */ + NV_ENC_PIC_PARAMS_H264_EXT + h264ExtPicParams; /**< [in]: Specifies the H264 extension config + parameters using this config. */ + uint32_t reserved[210]; /**< [in]: Reserved and must be set to 0. */ + void* reserved2[61]; /**< [in]: Reserved and must be set to NULL. */ +} NV_ENC_PIC_PARAMS_H264; + +/** + * \struct _NV_ENC_PIC_PARAMS_HEVC + * HEVC specific enc pic params. sent on a per frame basis. + */ +typedef struct _NV_ENC_PIC_PARAMS_HEVC { + uint32_t displayPOCSyntax; /**< [in]: Specifies the display POC syntax This is + required to be set if client is handling the + picture type decision. */ + uint32_t + refPicFlag; /**< [in]: Set to 1 for a reference picture. This is ignored + if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */ + uint32_t temporalId; /**< [in]: Specifies the temporal id of the picture */ + uint32_t + forceIntraRefreshWithFrameCnt; /**< [in]: Forces an intra refresh with + duration equal to intraRefreshFrameCnt. + When outputRecoveryPointSEI is + set this is value is used for + recovery_frame_cnt in recovery point SEI + message forceIntraRefreshWithFrameCnt + cannot be used if B frames are used in + the GOP structure specified */ + uint32_t + constrainedFrame : 1; /**< [in]: Set to 1 if client wants to encode + this frame with each slice completely + independent of other slices in the frame. + NV_ENC_INITIALIZE_PARAMS::enableConstrainedEncoding + should be set to 1 */ + uint32_t + sliceModeDataUpdate : 1; /**< [in]: Set to 1 if client wants to change the + sliceModeData field to specify new sliceSize + Parameter When forceIntraRefreshWithFrameCnt + is set it will have priority over sliceMode + setting */ + uint32_t ltrMarkFrame : 1; /**< [in]: Set to 1 if client wants to mark this + frame as LTR */ + uint32_t + ltrUseFrames : 1; /**< [in]: Set to 1 if client allows encoding this frame + using the LTR frames specified in ltrFrameBitmap */ + uint32_t reservedBitFields : 28; /**< [in]: Reserved bit fields and must be + set to 0 */ + uint8_t* + sliceTypeData; /**< [in]: Array which specifies the slice type used to + force intra slice for a particular slice. Currently + supported only for NV_ENC_CONFIG_H264::sliceMode == 3. + Client should allocate array of size + sliceModeData where sliceModeData is specified in field + of ::_NV_ENC_CONFIG_H264 Array element with index n + corresponds to nth slice. To force a particular slice to + intra client should set corresponding array element to + NV_ENC_SLICE_TYPE_I all other array elements should be + set to NV_ENC_SLICE_TYPE_DEFAULT */ + uint32_t sliceTypeArrayCnt; /**< [in]: Client should set this to the number of + elements allocated in sliceTypeData array. If + sliceTypeData is NULL then this should be set + to 0 */ + uint32_t + sliceMode; /**< [in]: This parameter in conjunction with sliceModeData + specifies the way in which the picture is divided into + slices sliceMode = 0 CTU based slices, sliceMode = 1 Byte + based slices, sliceMode = 2 CTU row based slices, sliceMode + = 3, numSlices in Picture When forceIntraRefreshWithFrameCnt + is set it will have priority over sliceMode setting When + sliceMode == 0 and sliceModeData == 0 whole picture will be + coded with one slice */ + uint32_t + sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. + For: sliceMode = 0, sliceModeData specifies # of CTUs in + each slice (except last slice) sliceMode = 1, + sliceModeData specifies maximum # of bytes in each slice + (except last slice) sliceMode = 2, sliceModeData + specifies # of CTU rows in each slice (except last + slice) sliceMode = 3, sliceModeData specifies number of + slices in the picture. Driver will divide picture into + slices optimally */ + uint32_t ltrMarkFrameIdx; /**< [in]: Specifies the long term reference frame + index to use for marking this frame as LTR.*/ + uint32_t + ltrUseFrameBitmap; /**< [in]: Specifies the associated bitmap of LTR frame + indices to use when encoding this frame. */ + uint32_t ltrUsageMode; /**< [in]: Not supported. Reserved for future use and + must be set to 0. */ + uint32_t seiPayloadArrayCnt; /**< [in]: Specifies the number of elements + allocated in seiPayloadArray array. */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0. */ + NV_ENC_SEI_PAYLOAD* seiPayloadArray; /**< [in]: Array of SEI payloads which + will be inserted for this frame. */ + uint32_t reserved2[244]; /**< [in]: Reserved and must be set to 0. */ + void* reserved3[61]; /**< [in]: Reserved and must be set to NULL. */ +} NV_ENC_PIC_PARAMS_HEVC; + +/** + * Codec specific per-picture encoding parameters. + */ +typedef union _NV_ENC_CODEC_PIC_PARAMS { + NV_ENC_PIC_PARAMS_H264 + h264PicParams; /**< [in]: H264 encode picture params. */ + NV_ENC_PIC_PARAMS_HEVC + hevcPicParams; /**< [in]: HEVC encode picture params. */ + uint32_t reserved[256]; /**< [in]: Reserved and must be set to 0. */ +} NV_ENC_CODEC_PIC_PARAMS; + +/** + * \struct _NV_ENC_PIC_PARAMS + * Encoding parameters that need to be sent on a per frame basis. + */ +typedef struct _NV_ENC_PIC_PARAMS { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_PIC_PARAMS_VER. */ + uint32_t inputWidth; /**< [in]: Specifies the input frame width */ + uint32_t inputHeight; /**< [in]: Specifies the input frame height */ + uint32_t inputPitch; /**< [in]: Specifies the input buffer pitch. If pitch + value is not known, set this to inputWidth. */ + uint32_t encodePicFlags; /**< [in]: Specifies bit-wise OR of encode picture + flags. See ::NV_ENC_PIC_FLAGS enum. */ + uint32_t frameIdx; /**< [in]: Specifies the frame index associated with the + input frame [optional]. */ + uint64_t + inputTimeStamp; /**< [in]: Specifies opaque data which is associated with + the encoded frame, but not actually encoded in the + output bitstream. This opaque data can be used later to + uniquely refer to the corresponding encoded frame. For + example, it can be used for identifying the frame to be + invalidated in the reference picture buffer, if lost at + the client. */ + uint64_t inputDuration; /**< [in]: Specifies duration of the input picture */ + NV_ENC_INPUT_PTR + inputBuffer; /**< [in]: Specifies the input buffer pointer. Client must + use a pointer obtained from ::NvEncCreateInputBuffer() or + ::NvEncMapInputResource() APIs.*/ + NV_ENC_OUTPUT_PTR + outputBitstream; /**< [in]: Specifies the output buffer pointer. + If + NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is set + to 0, specifies the pointer to output buffer. Client + should use a pointer obtained from + ::NvEncCreateBitstreamBuffer() API. If + NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is set + to 1, client should allocate buffer in video memory + for NV_ENC_ENCODE_OUT_PARAMS struct and encoded + bitstream data. Client should use a pointer obtained + from ::NvEncMapInputResource() API, when mapping this + output buffer and assign it to + NV_ENC_PIC_PARAMS::outputBitstream. First 256 bytes of + this buffer should be interpreted as + NV_ENC_ENCODE_OUT_PARAMS struct followed by encoded + bitstream data. Recommended size for output buffer is + sum of size of NV_ENC_ENCODE_OUT_PARAMS struct and + twice the input frame size for lower resolution eg. + CIF and 1.5 times the input frame size for higher + resolutions. If encoded bitstream size is greater than + the allocated buffer size for encoded bitstream, then + the output buffer will have encoded bitstream data + equal to buffer size. All CUDA operations on this + buffer must use the default stream. */ + void* + completionEvent; /**< [in]: Specifies an event to be signaled on + completion of encoding of this Frame [only if + operating in Asynchronous mode]. Each output buffer + should be associated with a distinct event pointer. */ + NV_ENC_BUFFER_FORMAT + bufferFmt; /**< [in]: Specifies the input buffer format. */ + NV_ENC_PIC_STRUCT + pictureStruct; /**< [in]: Specifies structure of the input picture. */ + NV_ENC_PIC_TYPE + pictureType; /**< [in]: Specifies input picture type. Client required to + be set explicitly by the client if the client has not set + NV_ENC_INITALIZE_PARAMS::enablePTD to 1 while calling + NvInitializeEncoder. */ + NV_ENC_CODEC_PIC_PARAMS + codecPicParams; /**< [in]: Specifies the codec specific per-picture + encoding parameters. */ + NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE meHintCountsPerBlock + [2]; /**< [in]: Specifies the number of hint candidates per block per + direction for the current frame. meHintCountsPerBlock[0] is for L0 + predictors and meHintCountsPerBlock[1] is for L1 predictors. The + candidate count in NV_ENC_PIC_PARAMS::meHintCountsPerBlock[lx] + must never exceed + NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[lx] provided + during encoder initialization. */ + NVENC_EXTERNAL_ME_HINT* + meExternalHints; /**< [in]: Specifies the pointer to ME external hints for + the current frame. The size of ME hint buffer should + be equal to number of macroblocks * the total number + of candidates per macroblock. The total number of + candidates per MB per direction = + 1*meHintCountsPerBlock[Lx].numCandsPerBlk16x16 + + 2*meHintCountsPerBlock[Lx].numCandsPerBlk16x8 + + 2*meHintCountsPerBlock[Lx].numCandsPerBlk8x8 + + + 4*meHintCountsPerBlock[Lx].numCandsPerBlk8x8. For + frames using bidirectional ME , the total number of + candidates for single macroblock is sum of total + number of candidates per MB for each direction (L0 and + L1) */ + uint32_t reserved1[6]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[2]; /**< [in]: Reserved and must be set to NULL */ + int8_t* + qpDeltaMap; /**< [in]: Specifies the pointer to signed byte array + containing value per MB for H264 and per CTB for HEVC in + raster scan order for the current picture, which will be + interpreted depending on NV_ENC_RC_PARAMS::qpMapMode. If + NV_ENC_RC_PARAMS::qpMapMode is NV_ENC_QP_MAP_DELTA, + qpDeltaMap specifies QP modifier per MB for H264 and per + CTB for HEVC. This QP modifier will be applied on top of + the QP chosen by rate control. If + NV_ENC_RC_PARAMS::qpMapMode is NV_ENC_QP_MAP_EMPHASIS, + qpDeltaMap specifies Emphasis Level Map per MB for H264. + This level value along with QP chosen by rate control is + used to compute the QP modifier, which in turn is + applied on top of QP chosen by rate control. If + NV_ENC_RC_PARAMS::qpMapMode is NV_ENC_QP_MAP_DISABLED, + value in qpDeltaMap will be ignored.*/ + uint32_t qpDeltaMapSize; /**< [in]: Specifies the size in bytes of qpDeltaMap + surface allocated by client and pointed to by + NV_ENC_PIC_PARAMS::qpDeltaMap. Surface (array) + should be picWidthInMbs * picHeightInMbs for H264 + and picWidthInCtbs * picHeightInCtbs for HEVC */ + uint32_t + reservedBitFields; /**< [in]: Reserved bitfields and must be set to 0 */ + uint16_t meHintRefPicDist + [2]; /**< [in]: Specifies temporal distance for reference picture + (NVENC_EXTERNAL_ME_HINT::refidx = 0) used during external ME with + NV_ENC_INITALIZE_PARAMS::enablePTD = 1 . meHintRefPicDist[0] is + for L0 hints and meHintRefPicDist[1] is for L1 hints. If not set, + will internally infer distance of 1. Ignored for + NV_ENC_INITALIZE_PARAMS::enablePTD = 0 */ + NV_ENC_INPUT_PTR + alphaBuffer; /**< [in]: Specifies the input alpha buffer pointer. Client + must use a pointer obtained from + ::NvEncCreateInputBuffer() or ::NvEncMapInputResource() + APIs. Applicable only when encoding hevc with alpha layer + is enabled. */ + uint32_t reserved3[286]; /**< [in]: Reserved and must be set to 0 */ + void* reserved4[59]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_PIC_PARAMS; + +/** Macro for constructing the version field of ::_NV_ENC_PIC_PARAMS */ +#define NV_ENC_PIC_PARAMS_VER (NVENCAPI_STRUCT_VERSION(4) | (1 << 31)) + +/** + * \struct _NV_ENC_MEONLY_PARAMS + * MEOnly parameters that need to be sent on a per motion estimation basis. + * NV_ENC_MEONLY_PARAMS::meExternalHints is supported for H264 only. + */ +typedef struct _NV_ENC_MEONLY_PARAMS { + uint32_t version; /**< [in]: Struct version. Must be set to + NV_ENC_MEONLY_PARAMS_VER.*/ + uint32_t inputWidth; /**< [in]: Specifies the input frame width */ + uint32_t inputHeight; /**< [in]: Specifies the input frame height */ + NV_ENC_INPUT_PTR + inputBuffer; /**< [in]: Specifies the input buffer pointer. Client must + use a pointer obtained from NvEncCreateInputBuffer() or + NvEncMapInputResource() APIs. */ + NV_ENC_INPUT_PTR + referenceFrame; /**< [in]: Specifies the reference frame pointer */ + NV_ENC_OUTPUT_PTR + mvBuffer; /**< [in]: Specifies the output buffer pointer. + If NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is + set to 0, specifies the pointer to motion vector data buffer + allocated by NvEncCreateMVBuffer. Client must lock mvBuffer + using ::NvEncLockBitstream() API to get the motion vector + data. If NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is + set to 1, client should allocate buffer in video memory for + storing the motion vector data. The size of this buffer must + be equal to total number of macroblocks multiplied by + size of NV_ENC_H264_MV_DATA struct. Client should use a + pointer obtained from ::NvEncMapInputResource() API, when + mapping this output buffer and assign it to + NV_ENC_MEONLY_PARAMS::mvBuffer. All CUDA operations on this + buffer must use the default stream. */ + NV_ENC_BUFFER_FORMAT + bufferFmt; /**< [in]: Specifies the input buffer format. */ + void* + completionEvent; /**< [in]: Specifies an event to be signaled on + completion of motion estimation of this Frame [only if + operating in Asynchronous mode]. Each output buffer + should be associated with a distinct event pointer. */ + uint32_t viewID; /**< [in]: Specifies left or right viewID if + NV_ENC_CONFIG_H264_MEONLY::bStereoEnable is set. viewID + can be 0,1 if bStereoEnable is set, 0 otherwise. */ + NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE + meHintCountsPerBlock + [2]; /**< [in]: Specifies the number of hint candidates per block for the + current frame. meHintCountsPerBlock[0] is for L0 predictors. The + candidate count in NV_ENC_PIC_PARAMS::meHintCountsPerBlock[lx] + must never exceed + NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[lx] provided + during encoder initialization. */ + NVENC_EXTERNAL_ME_HINT* + meExternalHints; /**< [in]: Specifies the pointer to ME external hints for + the current frame. The size of ME hint buffer should + be equal to number of macroblocks * the total number + of candidates per macroblock. The total number of + candidates per MB per direction = + 1*meHintCountsPerBlock[Lx].numCandsPerBlk16x16 + + 2*meHintCountsPerBlock[Lx].numCandsPerBlk16x8 + + 2*meHintCountsPerBlock[Lx].numCandsPerBlk8x8 + + + 4*meHintCountsPerBlock[Lx].numCandsPerBlk8x8. For + frames using bidirectional ME , the total number of + candidates for single macroblock is sum of total + number of candidates per MB for each direction (L0 and + L1) */ + uint32_t reserved1[243]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[59]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_MEONLY_PARAMS; + +/** NV_ENC_MEONLY_PARAMS struct version*/ +#define NV_ENC_MEONLY_PARAMS_VER NVENCAPI_STRUCT_VERSION(3) + +/** + * \struct _NV_ENC_LOCK_BITSTREAM + * Bitstream buffer lock parameters. + */ +typedef struct _NV_ENC_LOCK_BITSTREAM { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_LOCK_BITSTREAM_VER. */ + uint32_t doNotWait : 1; /**< [in]: If this flag is set, the NvEncodeAPI + interface will return buffer pointer even if + operation is not completed. If not set, the call + will block until operation completes. */ + uint32_t ltrFrame : 1; /**< [out]: Flag indicating this frame is marked as LTR + frame */ + uint32_t getRCStats : 1; /**< [in]: If this flag is set then lockBitstream + call will add additional intra-inter MB count and + average MVX, MVY */ + uint32_t reservedBitFields : 29; /**< [in]: Reserved bit fields and must be + set to 0 */ + void* outputBitstream; /**< [in]: Pointer to the bitstream buffer being + locked. */ + uint32_t* + sliceOffsets; /**< [in, out]: Array which receives the slice offsets. This + is not supported if NV_ENC_CONFIG_H264::sliceMode is 1 on + Kepler GPUs. Array size must be equal to size of frame in + MBs. */ + uint32_t frameIdx; /**< [out]: Frame no. for which the bitstream is being + retrieved. */ + uint32_t hwEncodeStatus; /**< [out]: The NvEncodeAPI interface status for the + locked picture. */ + uint32_t + numSlices; /**< [out]: Number of slices in the encoded picture. Will be + reported only if + NV_ENC_INITIALIZE_PARAMS::reportSliceOffsets set to 1. */ + uint32_t + bitstreamSizeInBytes; /**< [out]: Actual number of bytes generated and + copied to the memory pointed by + bitstreamBufferPtr. When HEVC alpha layer + encoding is enabled, this field reports the total + encoded size in bytes i.e it is the encoded size + of the base plus the alpha layer. */ + uint64_t outputTimeStamp; /**< [out]: Presentation timestamp associated with + the encoded output. */ + uint64_t outputDuration; /**< [out]: Presentation duration associates with the + encoded output. */ + void* bitstreamBufferPtr; /**< [out]: Pointer to the generated output + bitstream. For MEOnly mode + _NV_ENC_LOCK_BITSTREAM::bitstreamBufferPtr should + be typecast to + NV_ENC_H264_MV_DATA/NV_ENC_HEVC_MV_DATA + pointer respectively for H264/HEVC */ + NV_ENC_PIC_TYPE + pictureType; /**< [out]: Picture type of the encoded picture. */ + NV_ENC_PIC_STRUCT + pictureStruct; /**< [out]: Structure of the generated output picture. */ + uint32_t frameAvgQP; /**< [out]: Average QP of the frame. */ + uint32_t frameSatd; /**< [out]: Total SATD cost for whole frame. */ + uint32_t + ltrFrameIdx; /**< [out]: Frame index associated with this LTR frame. */ + uint32_t ltrFrameBitmap; /**< [out]: Bitmap of LTR frames indices which were + used for encoding this frame. Value of 0 if no LTR + frames were used. */ + uint32_t reserved[13]; /**< [in]: Reserved and must be set to 0 */ + uint32_t intraMBCount; /**< [out]: For H264, Number of Intra MBs in the + encoded frame. For HEVC, Number of Intra CTBs in the + encoded frame. Supported only if + _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */ + uint32_t interMBCount; /**< [out]: For H264, Number of Inter MBs in the + encoded frame, includes skip MBs. For HEVC, Number + of Inter CTBs in the encoded frame. Supported only + if _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */ + int32_t averageMVX; /**< [out]: Average Motion Vector in X direction for the + encoded frame. Supported only if + _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */ + int32_t averageMVY; /**< [out]: Average Motion Vector in y direction for the + encoded frame. Supported only if + _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */ + uint32_t + alphaLayerSizeInBytes; /**< [out]: Number of bytes generated for the alpha + layer in the encoded output. Applicable only + when HEVC with alpha encoding is enabled. */ + + uint32_t reserved1[218]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_LOCK_BITSTREAM; + +/** Macro for constructing the version field of ::_NV_ENC_LOCK_BITSTREAM */ +#define NV_ENC_LOCK_BITSTREAM_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * \struct _NV_ENC_LOCK_INPUT_BUFFER + * Uncompressed Input Buffer lock parameters. + */ +typedef struct _NV_ENC_LOCK_INPUT_BUFFER { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_LOCK_INPUT_BUFFER_VER. */ + uint32_t doNotWait : 1; /**< [in]: Set to 1 to make ::NvEncLockInputBuffer() + a unblocking call. If the encoding is not + completed, driver will return + ::NV_ENC_ERR_ENCODER_BUSY error code. */ + uint32_t reservedBitFields : 31; /**< [in]: Reserved bitfields and must be + set to 0 */ + NV_ENC_INPUT_PTR inputBuffer; /**< [in]: Pointer to the input buffer to be + locked, client should pass the pointer + obtained from ::NvEncCreateInputBuffer() or + ::NvEncMapInputResource API. */ + void* bufferDataPtr; /**< [out]: Pointed to the locked input buffer data. + Client can only access input buffer using the \p + bufferDataPtr. */ + uint32_t pitch; /**< [out]: Pitch of the locked input buffer. */ + uint32_t reserved1[251]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_LOCK_INPUT_BUFFER; + +/** Macro for constructing the version field of ::_NV_ENC_LOCK_INPUT_BUFFER */ +#define NV_ENC_LOCK_INPUT_BUFFER_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * \struct _NV_ENC_MAP_INPUT_RESOURCE + * Map an input resource to a Nvidia Encoder Input Buffer + */ +typedef struct _NV_ENC_MAP_INPUT_RESOURCE { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_MAP_INPUT_RESOURCE_VER. */ + uint32_t subResourceIndex; /**< [in]: Deprecated. Do not use. */ + void* inputResource; /**< [in]: Deprecated. Do not use. */ + NV_ENC_REGISTERED_PTR + registeredResource; /**< [in]: The Registered resource handle obtained by + calling NvEncRegisterInputResource. */ + NV_ENC_INPUT_PTR + mappedResource; /**< [out]: Mapped pointer corresponding to the + registeredResource. This pointer must be used in + NV_ENC_PIC_PARAMS::inputBuffer parameter in + ::NvEncEncodePicture() API. */ + NV_ENC_BUFFER_FORMAT + mappedBufferFmt; /**< [out]: Buffer format of the outputResource. This + buffer format must be used in + NV_ENC_PIC_PARAMS::bufferFmt if client using the above + mapped resource pointer. */ + uint32_t reserved1[251]; /**< [in]: Reserved and must be set to 0. */ + void* reserved2[63]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_MAP_INPUT_RESOURCE; + +/** Macro for constructing the version field of ::_NV_ENC_MAP_INPUT_RESOURCE */ +#define NV_ENC_MAP_INPUT_RESOURCE_VER NVENCAPI_STRUCT_VERSION(4) + +/** + * \struct _NV_ENC_INPUT_RESOURCE_OPENGL_TEX + * NV_ENC_REGISTER_RESOURCE::resourceToRegister must be a pointer to a variable + * of this type, when NV_ENC_REGISTER_RESOURCE::resourceType is + * NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX + */ +typedef struct _NV_ENC_INPUT_RESOURCE_OPENGL_TEX { + uint32_t texture; /**< [in]: The name of the texture to be used. */ + uint32_t target; /**< [in]: Accepted values are GL_TEXTURE_RECTANGLE and + GL_TEXTURE_2D. */ +} NV_ENC_INPUT_RESOURCE_OPENGL_TEX; + +/** + * \struct _NV_ENC_REGISTER_RESOURCE + * Register a resource for future use with the Nvidia Video Encoder Interface. + */ +typedef struct _NV_ENC_REGISTER_RESOURCE { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_REGISTER_RESOURCE_VER. */ + NV_ENC_INPUT_RESOURCE_TYPE + resourceType; /**< [in]: Specifies the type of resource to be registered. + Supported values are + ::NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX, + ::NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR, + ::NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX */ + uint32_t width; /**< [in]: Input frame width. */ + uint32_t height; /**< [in]: Input frame height. */ + uint32_t + pitch; /**< [in]: Input buffer pitch. + For ::NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX resources, set + this to 0. For + ::NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR resources, set this + to the pitch as obtained from cuMemAllocPitch(), + or to the width in bytes (if this resource was + created by using cuMemAlloc()). This value must + be a multiple of 4. For + ::NV_ENC_INPUT_RESOURCE_TYPE_CUDAARRAY resources, set this to + the width of the allocation in bytes (i.e. + CUDA_ARRAY3D_DESCRIPTOR::Width * + CUDA_ARRAY3D_DESCRIPTOR::NumChannels). For + ::NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX resources, set this to + the texture width multiplied by the number of + components in the texture format. */ + uint32_t subResourceIndex; /**< [in]: Subresource Index of the DirectX + resource to be registered. Should be set to 0 + for other interfaces. */ + void* resourceToRegister; /**< [in]: Handle to the resource that is being + registered. */ + NV_ENC_REGISTERED_PTR + registeredResource; /**< [out]: Registered resource handle. This should be + used in future interactions with the Nvidia Video + Encoder Interface. */ + NV_ENC_BUFFER_FORMAT + bufferFormat; /**< [in]: Buffer format of resource to be registered. */ + NV_ENC_BUFFER_USAGE + bufferUsage; /**< [in]: Usage of resource to be registered. */ + uint32_t reserved1[247]; /**< [in]: Reserved and must be set to 0. */ + void* reserved2[62]; /**< [in]: Reserved and must be set to NULL. */ +} NV_ENC_REGISTER_RESOURCE; + +/** Macro for constructing the version field of ::_NV_ENC_REGISTER_RESOURCE */ +#define NV_ENC_REGISTER_RESOURCE_VER NVENCAPI_STRUCT_VERSION(3) + +/** + * \struct _NV_ENC_STAT + * Encode Stats structure. + */ +typedef struct _NV_ENC_STAT { + uint32_t + version; /**< [in]: Struct version. Must be set to ::NV_ENC_STAT_VER. */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0 */ + NV_ENC_OUTPUT_PTR + outputBitStream; /**< [out]: Specifies the pointer to output bitstream. */ + uint32_t bitStreamSize; /**< [out]: Size of generated bitstream in bytes. */ + uint32_t picType; /**< [out]: Picture type of encoded picture. See + ::NV_ENC_PIC_TYPE. */ + uint32_t lastValidByteOffset; /**< [out]: Offset of last valid bytes of + completed bitstream */ + uint32_t sliceOffsets[16]; /**< [out]: Offsets of each slice */ + uint32_t picIdx; /**< [out]: Picture number */ + uint32_t frameAvgQP; /**< [out]: Average QP of the frame. */ + uint32_t ltrFrame : 1; /**< [out]: Flag indicating this frame is marked as LTR + frame */ + uint32_t reservedBitFields : 31; /**< [in]: Reserved bit fields and must be + set to 0 */ + uint32_t + ltrFrameIdx; /**< [out]: Frame index associated with this LTR frame. */ + uint32_t intraMBCount; /**< [out]: For H264, Number of Intra MBs in the + encoded frame. For HEVC, Number of Intra CTBs in the + encoded frame. */ + uint32_t interMBCount; /**< [out]: For H264, Number of Inter MBs in the + encoded frame, includes skip MBs. For HEVC, Number + of Inter CTBs in the encoded frame. */ + int32_t averageMVX; /**< [out]: Average Motion Vector in X direction for the + encoded frame. */ + int32_t averageMVY; /**< [out]: Average Motion Vector in y direction for the + encoded frame. */ + uint32_t reserved1[226]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_STAT; + +/** Macro for constructing the version field of ::_NV_ENC_STAT */ +#define NV_ENC_STAT_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * \struct _NV_ENC_SEQUENCE_PARAM_PAYLOAD + * Sequence and picture paramaters payload. + */ +typedef struct _NV_ENC_SEQUENCE_PARAM_PAYLOAD { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_INITIALIZE_PARAMS_VER. */ + uint32_t inBufferSize; /**< [in]: Specifies the size of the spsppsBuffer + provided by the client */ + uint32_t spsId; /**< [in]: Specifies the SPS id to be used in sequence + header. Default value is 0. */ + uint32_t ppsId; /**< [in]: Specifies the PPS id to be used in picture header. + Default value is 0. */ + void* spsppsBuffer; /**< [in]: Specifies bitstream header pointer of size + NV_ENC_SEQUENCE_PARAM_PAYLOAD::inBufferSize. It is the + client's responsibility to manage this memory. */ + uint32_t* outSPSPPSPayloadSize; /**< [out]: Size of the sequence and picture + header in bytes. */ + uint32_t reserved[250]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_SEQUENCE_PARAM_PAYLOAD; + +/** Macro for constructing the version field of ::_NV_ENC_SEQUENCE_PARAM_PAYLOAD + */ +#define NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * Event registration/unregistration parameters. + */ +typedef struct _NV_ENC_EVENT_PARAMS { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_EVENT_PARAMS_VER. */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0 */ + void* + completionEvent; /**< [in]: Handle to event to be registered/unregistered + with the NvEncodeAPI interface. */ + uint32_t reserved1[253]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_EVENT_PARAMS; + +/** Macro for constructing the version field of ::_NV_ENC_EVENT_PARAMS */ +#define NV_ENC_EVENT_PARAMS_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * Encoder Session Creation parameters + */ +typedef struct _NV_ENC_OPEN_ENCODE_SESSIONEX_PARAMS { + uint32_t version; /**< [in]: Struct version. Must be set to + ::NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER. */ + NV_ENC_DEVICE_TYPE deviceType; /**< [in]: Specified the device Type */ + void* device; /**< [in]: Pointer to client device. */ + void* reserved; /**< [in]: Reserved and must be set to 0. */ + uint32_t + apiVersion; /**< [in]: API version. Should be set to NVENCAPI_VERSION. */ + uint32_t reserved1[253]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS; +/** Macro for constructing the version field of + * ::_NV_ENC_OPEN_ENCODE_SESSIONEX_PARAMS */ +#define NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER NVENCAPI_STRUCT_VERSION(1) + +/** @} */ /* END ENCODER_STRUCTURE */ + +/** + * \addtogroup ENCODE_FUNC NvEncodeAPI Functions + * @{ + */ + +// NvEncOpenEncodeSession +/** + * \brief Opens an encoding session. + * + * Deprecated. + * + * \return + * ::NV_ENC_ERR_INVALID_CALL\n + * + */ +NVENCSTATUS NVENCAPI NvEncOpenEncodeSession(void* device, uint32_t deviceType, + void** encoder); + +// NvEncGetEncodeGuidCount +/** + * \brief Retrieves the number of supported encode GUIDs. + * + * The function returns the number of codec GUIDs supported by the NvEncodeAPI + * interface. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [out] encodeGUIDCount + * Number of supported encode GUIDs. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeGUIDCount(void* encoder, + uint32_t* encodeGUIDCount); + +// NvEncGetEncodeGUIDs +/** + * \brief Retrieves an array of supported encoder codec GUIDs. + * + * The function returns an array of codec GUIDs supported by the NvEncodeAPI + * interface. The client must allocate an array where the NvEncodeAPI interface + * can fill the supported GUIDs and pass the pointer in \p *GUIDs parameter. The + * size of the array can be determined by using ::NvEncGetEncodeGUIDCount() API. + * The Nvidia Encoding interface returns the number of codec GUIDs it has + * actually filled in the GUID array in the \p GUIDCount parameter. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] guidArraySize + * Number of GUIDs to retrieved. Should be set to the number retrieved using + * ::NvEncGetEncodeGUIDCount. + * \param [out] GUIDs + * Array of supported Encode GUIDs. + * \param [out] GUIDCount + * Number of supported Encode GUIDs. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeGUIDs(void* encoder, GUID* GUIDs, + uint32_t guidArraySize, + uint32_t* GUIDCount); + +// NvEncGetEncodeProfileGuidCount +/** + * \brief Retrieves the number of supported profile GUIDs. + * + * The function returns the number of profile GUIDs supported for a given codec. + * The client must first enumerate the codec GUIDs supported by the NvEncodeAPI + * interface. After determining the codec GUID, it can query the NvEncodeAPI + * interface to determine the number of profile GUIDs supported for a particular + * codec GUID. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * The codec GUID for which the profile GUIDs are being enumerated. + * \param [out] encodeProfileGUIDCount + * Number of encode profiles supported for the given encodeGUID. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeProfileGUIDCount( + void* encoder, GUID encodeGUID, uint32_t* encodeProfileGUIDCount); + +// NvEncGetEncodeProfileGUIDs +/** + * \brief Retrieves an array of supported encode profile GUIDs. + * + * The function returns an array of supported profile GUIDs for a particular + * codec GUID. The client must allocate an array where the NvEncodeAPI interface + * can populate the profile GUIDs. The client can determine the array size using + * ::NvEncGetEncodeProfileGUIDCount() API. The client must also validiate that + * the NvEncodeAPI interface supports the GUID the client wants to pass as \p + * encodeGUID parameter. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * The encode GUID whose profile GUIDs are being enumerated. + * \param [in] guidArraySize + * Number of GUIDs to be retrieved. Should be set to the number retrieved + * using + * ::NvEncGetEncodeProfileGUIDCount. + * \param [out] profileGUIDs + * Array of supported Encode Profile GUIDs + * \param [out] GUIDCount + * Number of valid encode profile GUIDs in \p profileGUIDs array. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeProfileGUIDs(void* encoder, GUID encodeGUID, + GUID* profileGUIDs, + uint32_t guidArraySize, + uint32_t* GUIDCount); + +// NvEncGetInputFormatCount +/** + * \brief Retrieve the number of supported Input formats. + * + * The function returns the number of supported input formats. The client must + * query the NvEncodeAPI interface to determine the supported input formats + * before creating the input surfaces. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the number of supported input formats + * is to be retrieved. + * \param [out] inputFmtCount + * Number of input formats supported for specified Encode GUID. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI NvEncGetInputFormatCount(void* encoder, GUID encodeGUID, + uint32_t* inputFmtCount); + +// NvEncGetInputFormats +/** + * \brief Retrieves an array of supported Input formats + * + * Returns an array of supported input formats The client must use the input + * format to create input surface using ::NvEncCreateInputBuffer() API. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the number of supported input formats + * is to be retrieved. + *\param [in] inputFmtArraySize + * Size input format count array passed in \p inputFmts. + *\param [out] inputFmts + * Array of input formats supported for this Encode GUID. + *\param [out] inputFmtCount + * The number of valid input format types returned by the NvEncodeAPI + * interface in \p inputFmts array. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetInputFormats(void* encoder, GUID encodeGUID, + NV_ENC_BUFFER_FORMAT* inputFmts, + uint32_t inputFmtArraySize, + uint32_t* inputFmtCount); + +// NvEncGetEncodeCaps +/** + * \brief Retrieves the capability value for a specified encoder attribute. + * + * The function returns the capability value for a given encoder attribute. The + * client must validate the encodeGUID using ::NvEncGetEncodeGUIDs() API before + * calling this function. The encoder attribute being queried are enumerated in + * ::NV_ENC_CAPS_PARAM enum. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the capability attribute is to be + * retrieved. \param [in] capsParam Used to specify attribute being queried. + * Refer ::NV_ENC_CAPS_PARAM for more details. \param [out] capsVal The value + * corresponding to the capability attribute being queried. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeCaps(void* encoder, GUID encodeGUID, + NV_ENC_CAPS_PARAM* capsParam, + int* capsVal); + +// NvEncGetEncodePresetCount +/** + * \brief Retrieves the number of supported preset GUIDs. + * + * The function returns the number of preset GUIDs available for a given codec. + * The client must validate the codec GUID using ::NvEncGetEncodeGUIDs() API + * before calling this function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the number of supported presets is to + * be retrieved. + * \param [out] encodePresetGUIDCount + * Receives the number of supported preset GUIDs. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodePresetCount(void* encoder, GUID encodeGUID, + uint32_t* encodePresetGUIDCount); + +// NvEncGetEncodePresetGUIDs +/** + * \brief Receives an array of supported encoder preset GUIDs. + * + * The function returns an array of encode preset GUIDs available for a given + * codec. The client can directly use one of the preset GUIDs based upon the use + * case or target device. The preset GUID chosen can be directly used in + * NV_ENC_INITIALIZE_PARAMS::presetGUID parameter to ::NvEncEncodePicture() API. + * Alternately client can also use the preset GUID to retrieve the encoding + * config parameters being used by NvEncodeAPI interface for that given preset, + * using + * ::NvEncGetEncodePresetConfig() API. It can then modify preset config + * parameters as per its use case and send it to NvEncodeAPI interface as part + * of NV_ENC_INITIALIZE_PARAMS::encodeConfig parameter for + * NvEncInitializeEncoder() API. + * + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the list of supported presets is to be + * retrieved. + * \param [in] guidArraySize + * Size of array of preset GUIDs passed in \p preset GUIDs + * \param [out] presetGUIDs + * Array of supported Encode preset GUIDs from the NvEncodeAPI interface + * to client. + * \param [out] encodePresetGUIDCount + * Receives the number of preset GUIDs returned by the NvEncodeAPI + * interface. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodePresetGUIDs(void* encoder, GUID encodeGUID, + GUID* presetGUIDs, + uint32_t guidArraySize, + uint32_t* encodePresetGUIDCount); + +// NvEncGetEncodePresetConfig +/** + * \brief Returns a preset config structure supported for given preset GUID. + * + * The function returns a preset config structure for a given preset GUID. + * Before using this function the client must enumerate the preset GUIDs + * available for a given codec. The preset config structure can be modified by + * the client depending upon its use case and can be then used to initialize the + * encoder using + * ::NvEncInitializeEncoder() API. The client can use this function only if it + * wants to modify the NvEncodeAPI preset configuration, otherwise it can + * directly use the preset GUID. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the list of supported presets is to be + * retrieved. + * \param [in] presetGUID + * Preset GUID, corresponding to which the Encoding configurations is to be + * retrieved. + * \param [out] presetConfig + * The requested Preset Encoder Attribute set. Refer ::_NV_ENC_CONFIG for + * more details. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI +NvEncGetEncodePresetConfig(void* encoder, GUID encodeGUID, GUID presetGUID, + NV_ENC_PRESET_CONFIG* presetConfig); + +// NvEncGetEncodePresetConfigEx +/** + * \brief Returns a preset config structure supported for given preset GUID. + * + * The function returns a preset config structure for a given preset GUID and + * tuning info. NvEncGetEncodePresetConfigEx() API is not applicable to H264 and + * HEVC meonly mode. Before using this function the client must enumerate the + * preset GUIDs available for a given codec. The preset config structure can be + * modified by the client depending upon its use case and can be then used to + * initialize the encoder using + * ::NvEncInitializeEncoder() API. The client can use this function only if it + * wants to modify the NvEncodeAPI preset configuration, otherwise it can + * directly use the preset GUID. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the list of supported presets is to be + * retrieved. + * \param [in] presetGUID + * Preset GUID, corresponding to which the Encoding configurations is to be + * retrieved. + * \param [in] tuningInfo + * tuning info, corresponding to which the Encoding configurations is to be + * retrieved. + * \param [out] presetConfig + * The requested Preset Encoder Attribute set. Refer ::_NV_ENC_CONFIG for + * more details. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodePresetConfigEx( + void* encoder, GUID encodeGUID, GUID presetGUID, + NV_ENC_TUNING_INFO tuningInfo, NV_ENC_PRESET_CONFIG* presetConfig); + +// NvEncInitializeEncoder +/** + * \brief Initialize the encoder. + * + * This API must be used to initialize the encoder. The initialization parameter + * is passed using \p *createEncodeParams The client must send the following + * fields of the _NV_ENC_INITIALIZE_PARAMS structure with a valid value. + * - NV_ENC_INITIALIZE_PARAMS::encodeGUID + * - NV_ENC_INITIALIZE_PARAMS::encodeWidth + * - NV_ENC_INITIALIZE_PARAMS::encodeHeight + * + * The client can pass a preset GUID directly to the NvEncodeAPI interface using + * NV_ENC_INITIALIZE_PARAMS::presetGUID field. If the client doesn't pass + * NV_ENC_INITIALIZE_PARAMS::encodeConfig structure, the codec specific + *parameters will be selected based on the preset GUID. The preset GUID must + *have been validated by the client using ::NvEncGetEncodePresetGUIDs() API. If + *the client passes a custom ::_NV_ENC_CONFIG structure through + * NV_ENC_INITIALIZE_PARAMS::encodeConfig , it will override the codec specific + *parameters based on the preset GUID. It is recommended that even if the client + *passes a custom config, it should also send a preset GUID. In this case, the + *preset GUID passed by the client will not override any of the custom config + *parameters programmed by the client, it is only used as a hint by the + *NvEncodeAPI interface to determine certain encoder parameters which are not + *exposed to the client. + * + * There are two modes of operation for the encoder namely: + * - Asynchronous mode + * - Synchronous mode + * + * The client can select asynchronous or synchronous mode by setting the \p + * enableEncodeAsync field in ::_NV_ENC_INITIALIZE_PARAMS to 1 or 0 + *respectively. \par Asynchronous mode of operation: The Asynchronous mode can + *be enabled by setting NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to 1. The + *client operating in asynchronous mode must allocate completion event object + * for each output buffer and pass the completion event object in the + * ::NvEncEncodePicture() API. The client can create another thread and wait on + * the event object to be signaled by NvEncodeAPI interface on completion of the + * encoding process for the output frame. This should unblock the main thread + *from submitting work to the encoder. When the event is signaled the client can + *call NvEncodeAPI interfaces to copy the bitstream data using + *::NvEncLockBitstream() API. This is the preferred mode of operation. + * + * NOTE: Asynchronous mode is not supported on Linux. + * + *\par Synchronous mode of operation: + * The client can select synchronous mode by setting + *NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to 0. The client working in + *synchronous mode can work in a single threaded or multi threaded mode. The + *client need not allocate any event objects. The client can only lock the + *bitstream data after NvEncodeAPI interface has returned + * ::NV_ENC_SUCCESS from encode picture. The NvEncodeAPI interface can return + * ::NV_ENC_ERR_NEED_MORE_INPUT error code from ::NvEncEncodePicture() API. The + * client must not lock the output buffer in such case but should send the next + * frame for encoding. The client must keep on calling ::NvEncEncodePicture() + *API until it returns ::NV_ENC_SUCCESS. \n The client must always lock the + *bitstream data in order in which it has submitted. This is true for both + *asynchronous and synchronous mode. + * + *\par Picture type decision: + * If the client is taking the picture type decision and it must disable the + *picture type decision module in NvEncodeAPI by setting + *NV_ENC_INITIALIZE_PARAMS::enablePTD to 0. In this case the client is required + *to send the picture in encoding order to NvEncodeAPI by doing the re-ordering + *for B frames. \n If the client doesn't want to take the picture type decision + *it can enable picture type decision module in the NvEncodeAPI interface by + *setting NV_ENC_INITIALIZE_PARAMS::enablePTD to 1 and send the input pictures + *in display order. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] createEncodeParams + * Refer ::_NV_ENC_INITIALIZE_PARAMS for details. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncInitializeEncoder( + void* encoder, NV_ENC_INITIALIZE_PARAMS* createEncodeParams); + +// NvEncCreateInputBuffer +/** + * \brief Allocates Input buffer. + * + * This function is used to allocate an input buffer. The client must enumerate + * the input buffer format before allocating the input buffer resources. The + * NV_ENC_INPUT_PTR returned by the NvEncodeAPI interface in the + * NV_ENC_CREATE_INPUT_BUFFER::inputBuffer field can be directly used in + * ::NvEncEncodePicture() API. The number of input buffers to be allocated by + * the client must be at least 4 more than the number of B frames being used for + * encoding. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] createInputBufferParams + * Pointer to the ::NV_ENC_CREATE_INPUT_BUFFER structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncCreateInputBuffer( + void* encoder, NV_ENC_CREATE_INPUT_BUFFER* createInputBufferParams); + +// NvEncDestroyInputBuffer +/** + * \brief Release an input buffers. + * + * This function is used to free an input buffer. If the client has allocated + * any input buffer using ::NvEncCreateInputBuffer() API, it must free those + * input buffers by calling this function. The client must release the input + * buffers before destroying the encoder using ::NvEncDestroyEncoder() API. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] inputBuffer + * Pointer to the input buffer to be released. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncDestroyInputBuffer(void* encoder, + NV_ENC_INPUT_PTR inputBuffer); + +// NvEncSetIOCudaStreams +/** + * \brief Set input and output CUDA stream for specified encoder attribute. + * + * Encoding may involve CUDA pre-processing on the input and post-processing on + * encoded output. This function is used to set input and output CUDA streams to + * pipeline the CUDA pre-processing and post-processing tasks. Clients should + * call this function before the call to NvEncUnlockInputBuffer(). If this + * function is not called, the default CUDA stream is used for input and output + * processing. After a successful call to this function, the streams specified + * in that call will replace the previously-used streams. + * This API is supported for NVCUVID interface only. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] inputStream + * Pointer to CUstream which is used to process + * ::NV_ENC_PIC_PARAMS::inputFrame for encode. In case of ME-only mode, + * inputStream is used to process ::NV_ENC_MEONLY_PARAMS::inputBuffer and + * ::NV_ENC_MEONLY_PARAMS::referenceFrame + * \param [in] outputStream + * Pointer to CUstream which is used to process + * ::NV_ENC_PIC_PARAMS::outputBuffer for encode. In case of ME-only mode, + * outputStream is used to process ::NV_ENC_MEONLY_PARAMS::mvBuffer + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI NvEncSetIOCudaStreams(void* encoder, + NV_ENC_CUSTREAM_PTR inputStream, + NV_ENC_CUSTREAM_PTR outputStream); + +// NvEncCreateBitstreamBuffer +/** + * \brief Allocates an output bitstream buffer + * + * This function is used to allocate an output bitstream buffer and returns a + * NV_ENC_OUTPUT_PTR to bitstream buffer to the client in the + * NV_ENC_CREATE_BITSTREAM_BUFFER::bitstreamBuffer field. + * The client can only call this function after the encoder session has been + * initialized using ::NvEncInitializeEncoder() API. The minimum number of + * output buffers allocated by the client must be at least 4 more than the + * number of B B frames being used for encoding. The client can only access the + * output bitstream data by locking the \p bitstreamBuffer using the + * ::NvEncLockBitstream() function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] createBitstreamBufferParams + * Pointer ::NV_ENC_CREATE_BITSTREAM_BUFFER for details. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncCreateBitstreamBuffer( + void* encoder, NV_ENC_CREATE_BITSTREAM_BUFFER* createBitstreamBufferParams); + +// NvEncDestroyBitstreamBuffer +/** + * \brief Release a bitstream buffer. + * + * This function is used to release the output bitstream buffer allocated using + * the ::NvEncCreateBitstreamBuffer() function. The client must release the + * output bitstreamBuffer using this function before destroying the encoder + * session. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] bitstreamBuffer + * Pointer to the bitstream buffer being released. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI +NvEncDestroyBitstreamBuffer(void* encoder, NV_ENC_OUTPUT_PTR bitstreamBuffer); + +// NvEncEncodePicture +/** + * \brief Submit an input picture for encoding. + * + * This function is used to submit an input picture buffer for encoding. The + * encoding parameters are passed using \p *encodePicParams which is a pointer + * to the ::_NV_ENC_PIC_PARAMS structure. + * + * If the client has set NV_ENC_INITIALIZE_PARAMS::enablePTD to 0, then it must + * send a valid value for the following fields. + * - NV_ENC_PIC_PARAMS::pictureType + * - NV_ENC_PIC_PARAMS_H264::displayPOCSyntax (H264 only) + * - NV_ENC_PIC_PARAMS_H264::frameNumSyntax(H264 only) + * - NV_ENC_PIC_PARAMS_H264::refPicFlag(H264 only) + * + *\par MVC Encoding: + * For MVC encoding the client must call encode picture API for each view + separately + * and must pass valid view id in NV_ENC_PIC_PARAMS_MVC::viewID field. Currently + * NvEncodeAPI only support stereo MVC so client must send viewID as 0 for base + * view and view ID as 1 for dependent view. + * + *\par Asynchronous Encoding + * If the client has enabled asynchronous mode of encoding by setting + * NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to 1 in the + ::NvEncInitializeEncoder() + * API ,then the client must send a valid NV_ENC_PIC_PARAMS::completionEvent. + * Incase of asynchronous mode of operation, client can queue the + ::NvEncEncodePicture() + * API commands from the main thread and then queue output buffers to be + processed + * to a secondary worker thread. Before the locking the output buffers in the + * secondary thread , the client must wait on NV_ENC_PIC_PARAMS::completionEvent + * it has queued in ::NvEncEncodePicture() API call. The client must always + process + * completion event and the output buffer in the same order in which they have + been + * submitted for encoding. The NvEncodeAPI interface is responsible for any + * re-ordering required for B frames and will always ensure that encoded + bitstream + * data is written in the same order in which output buffer is submitted. + * The NvEncodeAPI interface may return ::NV_ENC_ERR_NEED_MORE_INPUT error code + for + * some ::NvEncEncodePicture() API calls but the client must not treat it as a + fatal error. + * The NvEncodeAPI interface might not be able to submit an input picture buffer + for encoding + * immediately due to re-ordering for B frames. + *\code + The below example shows how asynchronous encoding in case of 1 B frames + ------------------------------------------------------------------------ + Suppose the client allocated 4 input buffers(I1,I2..), 4 output + buffers(O1,O2..) and 4 completion events(E1, E2, ...). The NvEncodeAPI + interface will need to keep a copy of the input buffers for re-ordering and it + allocates following internal buffers (NvI1, NvI2...). These internal buffers + are managed by NvEncodeAPI and the client is not responsible for the allocating + or freeing the memory of the internal buffers. + + a) The client main thread will queue the following encode frame calls. + Note the picture type is unknown to the client, the decision is being taken by + NvEncodeAPI interface. The client should pass ::_NV_ENC_PIC_PARAMS parameter + consisting of allocated input buffer, output buffer and output events in + successive + ::NvEncEncodePicture() API calls along with other required encode picture + params. For example: 1st EncodePicture parameters - (I1, O1, E1) 2nd + EncodePicture parameters - (I2, O2, E2) 3rd EncodePicture parameters - (I3, O3, + E3) + + b) NvEncodeAPI SW will receive the following encode Commands from the client. + The left side shows input from client in the form (Input buffer, Output + Buffer, Output Event). The right hand side shows a possible picture type + decision take by the NvEncodeAPI interface. (I1, O1, E1) ---P1 Frame (I2, + O2, E2) ---B2 Frame (I3, O3, E3) ---P3 Frame + + c) NvEncodeAPI interface will make a copy of the input buffers to its internal + buffers for re-ordering. These copies are done as part of nvEncEncodePicture + function call from the client and NvEncodeAPI interface is responsible for + synchronization of copy operation with the actual encoding operation. + I1 --> NvI1 + I2 --> NvI2 + I3 --> NvI3 + + d) The NvEncodeAPI encodes I1 as P frame and submits I1 to encoder HW and + returns ::NV_ENC_SUCCESS. The NvEncodeAPI tries to encode I2 as B frame and + fails with ::NV_ENC_ERR_NEED_MORE_INPUT error code. The error is not fatal and + it notifies client that I2 is not submitted to encoder immediately. The + NvEncodeAPI encodes I3 as P frame and submits I3 for encoding which will be + used as backward reference frame for I2. The NvEncodeAPI then submits I2 for + encoding and returns ::NV_ENC_SUCESS. Both the submission are part of the same + ::NvEncEncodePicture() function call. + + e) After returning from ::NvEncEncodePicture() call , the client must queue + the output bitstream processing work to the secondary thread. The output + bitstream processing for asynchronous mode consist of first waiting on + completion event(E1, E2..) and then locking the output bitstream buffer(O1, + O2..) for reading the encoded data. The work queued to the secondary thread by + the client is in the following order (I1, O1, E1) (I2, O2, E2) (I3, O3, E3) + Note they are in the same order in which client calls ::NvEncEncodePicture() + API in \p step a). + + f) NvEncodeAPI interface will do the re-ordering such that Encoder HW will + receive the following encode commands: (NvI1, O1, E1) ---P1 Frame (NvI3, O2, + E2) ---P3 Frame (NvI2, O3, E3) ---B2 frame + + g) After the encoding operations are completed, the events will be signaled + by NvEncodeAPI interface in the following order : + (O1, E1) ---P1 Frame ,output bitstream copied to O1 and event E1 signaled. + (O2, E2) ---P3 Frame ,output bitstream copied to O2 and event E2 signaled. + (O3, E3) ---B2 Frame ,output bitstream copied to O3 and event E3 signaled. + + h) The client must lock the bitstream data using ::NvEncLockBitstream() API in + the order O1,O2,O3 to read the encoded data, after waiting for the events + to be signaled in the same order i.e E1, E2 and E3.The output processing is + done in the secondary thread in the following order: + Waits on E1, copies encoded bitstream from O1 + Waits on E2, copies encoded bitstream from O2 + Waits on E3, copies encoded bitstream from O3 + + -Note the client will receive the events signaling and output buffer in the + same order in which they have submitted for encoding. + -Note the LockBitstream will have picture type field which will notify the + output picture type to the clients. + -Note the input, output buffer and the output completion event are free to be + reused once NvEncodeAPI interfaced has signaled the event and the client has + copied the data from the output buffer. + + * \endcode + * + *\par Synchronous Encoding + * The client can enable synchronous mode of encoding by setting + * NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to 0 in + ::NvEncInitializeEncoder() API. + * The NvEncodeAPI interface may return ::NV_ENC_ERR_NEED_MORE_INPUT error code + for + * some ::NvEncEncodePicture() API calls when + NV_ENC_INITIALIZE_PARAMS::enablePTD + * is set to 1, but the client must not treat it as a fatal error. The + NvEncodeAPI + * interface might not be able to submit an input picture buffer for encoding + * immediately due to re-ordering for B frames. The NvEncodeAPI interface cannot + * submit the input picture which is decided to be encoded as B frame as it + waits + * for backward reference from temporally subsequent frames. This input picture + * is buffered internally and waits for more input picture to arrive. The client + * must not call ::NvEncLockBitstream() API on the output buffers whose + * ::NvEncEncodePicture() API returns ::NV_ENC_ERR_NEED_MORE_INPUT. The client + must + * wait for the NvEncodeAPI interface to return ::NV_ENC_SUCCESS before locking + the + * output bitstreams to read the encoded bitstream data. The following example + * explains the scenario with synchronous encoding with 2 B frames. + *\code + The below example shows how synchronous encoding works in case of 1 B frames + ----------------------------------------------------------------------------- + Suppose the client allocated 4 input buffers(I1,I2..), 4 output + buffers(O1,O2..) and 4 completion events(E1, E2, ...). The NvEncodeAPI + interface will need to keep a copy of the input buffers for re-ordering and it + allocates following internal buffers (NvI1, NvI2...). These internal buffers + are managed by NvEncodeAPI and the client is not responsible for the allocating + or freeing the memory of the internal buffers. + + The client calls ::NvEncEncodePicture() API with input buffer I1 and output + buffer O1. The NvEncodeAPI decides to encode I1 as P frame and submits it to + encoder HW and returns ::NV_ENC_SUCCESS. The client can now read the encoded + data by locking the output O1 by calling NvEncLockBitstream API. + + The client calls ::NvEncEncodePicture() API with input buffer I2 and output + buffer O2. The NvEncodeAPI decides to encode I2 as B frame and buffers I2 by + copying it to internal buffer and returns ::NV_ENC_ERR_NEED_MORE_INPUT. The + error is not fatal and it notifies client that it cannot read the encoded data + by locking the output O2 by calling ::NvEncLockBitstream() API without + submitting more work to the NvEncodeAPI interface. + + The client calls ::NvEncEncodePicture() with input buffer I3 and output buffer + O3. The NvEncodeAPI decides to encode I3 as P frame and it first submits I3 for + encoding which will be used as backward reference frame for I2. + The NvEncodeAPI then submits I2 for encoding and returns ::NV_ENC_SUCESS. Both + the submission are part of the same ::NvEncEncodePicture() function call. + The client can now read the encoded data for both the frames by locking the + output O2 followed by O3 ,by calling ::NvEncLockBitstream() API. + + The client must always lock the output in the same order in which it has + submitted to receive the encoded bitstream in correct encoding order. + + * \endcode + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] encodePicParams + * Pointer to the ::_NV_ENC_PIC_PARAMS structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_ENCODER_BUSY \n + * ::NV_ENC_ERR_NEED_MORE_INPUT \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncEncodePicture(void* encoder, + NV_ENC_PIC_PARAMS* encodePicParams); + +// NvEncLockBitstream +/** + * \brief Lock output bitstream buffer + * + * This function is used to lock the bitstream buffer to read the encoded data. + * The client can only access the encoded data by calling this function. + * The pointer to client accessible encoded data is returned in the + * NV_ENC_LOCK_BITSTREAM::bitstreamBufferPtr field. The size of the encoded data + * in the output buffer is returned in the + * NV_ENC_LOCK_BITSTREAM::bitstreamSizeInBytes The NvEncodeAPI interface also + * returns the output picture type and picture structure of the encoded frame in + * NV_ENC_LOCK_BITSTREAM::pictureType and NV_ENC_LOCK_BITSTREAM::pictureStruct + * fields respectively. If the client has set NV_ENC_LOCK_BITSTREAM::doNotWait + * to 1, the function might return + * ::NV_ENC_ERR_LOCK_BUSY if client is operating in synchronous mode. This is + * not a fatal failure if NV_ENC_LOCK_BITSTREAM::doNotWait is set to 1. In the + * above case the client can retry the function after few milliseconds. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] lockBitstreamBufferParams + * Pointer to the ::_NV_ENC_LOCK_BITSTREAM structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_LOCK_BUSY \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncLockBitstream( + void* encoder, NV_ENC_LOCK_BITSTREAM* lockBitstreamBufferParams); + +// NvEncUnlockBitstream +/** + * \brief Unlock the output bitstream buffer + * + * This function is used to unlock the output bitstream buffer after the client + * has read the encoded data from output buffer. The client must call this + * function to unlock the output buffer which it has previously locked using + * ::NvEncLockBitstream() function. Using a locked bitstream buffer in + * ::NvEncEncodePicture() API will cause the function to fail. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] bitstreamBuffer + * bitstream buffer pointer being unlocked + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncUnlockBitstream(void* encoder, + NV_ENC_OUTPUT_PTR bitstreamBuffer); + +// NvLockInputBuffer +/** + * \brief Locks an input buffer + * + * This function is used to lock the input buffer to load the uncompressed YUV + * pixel data into input buffer memory. The client must pass the + * NV_ENC_INPUT_PTR it had previously allocated using + * ::NvEncCreateInputBuffer()in the NV_ENC_LOCK_INPUT_BUFFER::inputBuffer field. + * The NvEncodeAPI interface returns pointer to client accessible input buffer + * memory in NV_ENC_LOCK_INPUT_BUFFER::bufferDataPtr field. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] lockInputBufferParams + * Pointer to the ::_NV_ENC_LOCK_INPUT_BUFFER structure + * + * \return + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_LOCK_BUSY \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncLockInputBuffer( + void* encoder, NV_ENC_LOCK_INPUT_BUFFER* lockInputBufferParams); + +// NvUnlockInputBuffer +/** + * \brief Unlocks the input buffer + * + * This function is used to unlock the input buffer memory previously locked for + * uploading YUV pixel data. The input buffer must be unlocked before being used + * again for encoding, otherwise NvEncodeAPI will fail the + * ::NvEncEncodePicture() + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] inputBuffer + * Pointer to the input buffer that is being unlocked. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + * + */ +NVENCSTATUS NVENCAPI NvEncUnlockInputBuffer(void* encoder, + NV_ENC_INPUT_PTR inputBuffer); + +// NvEncGetEncodeStats +/** + * \brief Get encoding statistics. + * + * This function is used to retrieve the encoding statistics. + * This API is not supported when encode device type is CUDA. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] encodeStats + * Pointer to the ::_NV_ENC_STAT structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeStats(void* encoder, + NV_ENC_STAT* encodeStats); + +// NvEncGetSequenceParams +/** + * \brief Get encoded sequence and picture header. + * + * This function can be used to retrieve the sequence and picture header out of + * band. The client must call this function only after the encoder has been + * initialized using ::NvEncInitializeEncoder() function. The client must + * allocate the memory where the NvEncodeAPI interface can copy the bitstream + * header and pass the pointer to the memory in + * NV_ENC_SEQUENCE_PARAM_PAYLOAD::spsppsBuffer. The size of buffer is passed in + * the field NV_ENC_SEQUENCE_PARAM_PAYLOAD::inBufferSize. The NvEncodeAPI + * interface will copy the bitstream header payload and returns the actual size + * of the bitstream header in the field + * NV_ENC_SEQUENCE_PARAM_PAYLOAD::outSPSPPSPayloadSize. + * The client must call ::NvEncGetSequenceParams() function from the same + * thread which is being used to call ::NvEncEncodePicture() function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] sequenceParamPayload + * Pointer to the ::_NV_ENC_SEQUENCE_PARAM_PAYLOAD structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetSequenceParams( + void* encoder, NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload); + +// NvEncGetSequenceParamEx +/** + * \brief Get sequence and picture header. + * + * This function can be used to retrieve the sequence and picture header out of + * band, even when encoder has not been initialized using + * ::NvEncInitializeEncoder() function. The client must allocate the memory + * where the NvEncodeAPI interface can copy the bitstream header and pass the + * pointer to the memory in NV_ENC_SEQUENCE_PARAM_PAYLOAD::spsppsBuffer. The + * size of buffer is passed in the field + * NV_ENC_SEQUENCE_PARAM_PAYLOAD::inBufferSize. If encoder has not been + * initialized using ::NvEncInitializeEncoder() function, client must send + * NV_ENC_INITIALIZE_PARAMS as input. The NV_ENC_INITIALIZE_PARAMS passed must + * be same as the one which will be used for initializing encoder using + * ::NvEncInitializeEncoder() function later. If encoder is already initialized + * using ::NvEncInitializeEncoder() function, the provided + * NV_ENC_INITIALIZE_PARAMS structure is ignored. The NvEncodeAPI interface will + * copy the bitstream header payload and returns the actual size of the + * bitstream header in the field + * NV_ENC_SEQUENCE_PARAM_PAYLOAD::outSPSPPSPayloadSize. The client must call + * ::NvEncGetSequenceParamsEx() function from the same thread which is being + * used to call ::NvEncEncodePicture() function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encInitParams + * Pointer to the _NV_ENC_INITIALIZE_PARAMS structure. + * \param [in,out] sequenceParamPayload + * Pointer to the ::_NV_ENC_SEQUENCE_PARAM_PAYLOAD structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI +NvEncGetSequenceParamEx(void* encoder, NV_ENC_INITIALIZE_PARAMS* encInitParams, + NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload); + +// NvEncRegisterAsyncEvent +/** + * \brief Register event for notification to encoding completion. + * + * This function is used to register the completion event with NvEncodeAPI + * interface. The event is required when the client has configured the encoder + * to work in asynchronous mode. In this mode the client needs to send a + * completion event with every output buffer. The NvEncodeAPI interface will + * signal the completion of the encoding process using this event. Only after + * the event is signaled the client can get the encoded data using + * ::NvEncLockBitstream() function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] eventParams + * Pointer to the ::_NV_ENC_EVENT_PARAMS structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncRegisterAsyncEvent(void* encoder, + NV_ENC_EVENT_PARAMS* eventParams); + +// NvEncUnregisterAsyncEvent +/** + * \brief Unregister completion event. + * + * This function is used to unregister completion event which has been + * previously registered using ::NvEncRegisterAsyncEvent() function. The client + * must unregister all events before destroying the encoder using + * ::NvEncDestroyEncoder() function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] eventParams + * Pointer to the ::_NV_ENC_EVENT_PARAMS structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI +NvEncUnregisterAsyncEvent(void* encoder, NV_ENC_EVENT_PARAMS* eventParams); + +// NvEncMapInputResource +/** + * \brief Map an externally created input resource pointer for encoding. + * + * Maps an externally allocated input resource [using and returns a + * NV_ENC_INPUT_PTR which can be used for encoding in the ::NvEncEncodePicture() + * function. The mapped resource is returned in the field + * NV_ENC_MAP_INPUT_RESOURCE::outputResourcePtr. The NvEncodeAPI interface also + * returns the buffer format of the mapped resource in the field + * NV_ENC_MAP_INPUT_RESOURCE::outbufferFmt. This function provides + * synchronization guarantee that any graphics work submitted on the input + * buffer is completed before the buffer is used for encoding. This is also true + * for compute (i.e. CUDA) work, provided that the previous workload using the + * input resource was submitted to the default stream. The client should not + * access any input buffer while they are mapped by the encoder. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] mapInputResParams + * Pointer to the ::_NV_ENC_MAP_INPUT_RESOURCE structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_RESOURCE_NOT_REGISTERED \n + * ::NV_ENC_ERR_MAP_FAILED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncMapInputResource( + void* encoder, NV_ENC_MAP_INPUT_RESOURCE* mapInputResParams); + +// NvEncUnmapInputResource +/** + * \brief UnMaps a NV_ENC_INPUT_PTR which was mapped for encoding + * + * + * UnMaps an input buffer which was previously mapped using + * ::NvEncMapInputResource() API. The mapping created using + * ::NvEncMapInputResource() should be invalidated using this API before the + * external resource is destroyed by the client. The client must unmap the + * buffer after ::NvEncLockBitstream() API returns successfully for encode work + * submitted using the mapped input buffer. + * + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] mappedInputBuffer + * Pointer to the NV_ENC_INPUT_PTR + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_RESOURCE_NOT_REGISTERED \n + * ::NV_ENC_ERR_RESOURCE_NOT_MAPPED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI +NvEncUnmapInputResource(void* encoder, NV_ENC_INPUT_PTR mappedInputBuffer); + +// NvEncDestroyEncoder +/** + * \brief Destroy Encoding Session + * + * Destroys the encoder session previously created using + * ::NvEncOpenEncodeSession() function. The client must flush the encoder before + * freeing any resources. In order to flush the encoder the client must pass a + * NULL encode picture packet and either wait for the ::NvEncEncodePicture() + * function to return in synchronous mode or wait for the flush event to be + * signaled by the encoder in asynchronous mode. The client must free all the + * input and output resources created using the NvEncodeAPI interface before + * destroying the encoder. If the client is operating in asynchronous mode, it + * must also unregister the completion events previously registered. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncDestroyEncoder(void* encoder); + +// NvEncInvalidateRefFrames +/** + * \brief Invalidate reference frames + * + * Invalidates reference frame based on the time stamp provided by the client. + * The encoder marks any reference frames or any frames which have been + * reconstructed using the corrupt frame as invalid for motion estimation and + * uses older reference frames for motion estimation. The encoded forces the + * current frame to be encoded as an intra frame if no reference frames are left + * after invalidation process. This is useful for low latency application for + * error resiliency. The client is recommended to set + * NV_ENC_CONFIG_H264::maxNumRefFrames to a large value so that encoder can keep + * a backup of older reference frames in the DPB and can use them for motion + * estimation when the newer reference frames have been invalidated. This API + * can be called multiple times. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] invalidRefFrameTimeStamp + * Timestamp of the invalid reference frames which needs to be invalidated. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI +NvEncInvalidateRefFrames(void* encoder, uint64_t invalidRefFrameTimeStamp); + +// NvEncOpenEncodeSessionEx +/** + * \brief Opens an encoding session. + * + * Opens an encoding session and returns a pointer to the encoder interface in + * the \p **encoder parameter. The client should start encoding process by + * calling this API first. The client must pass a pointer to IDirect3DDevice9 + * device or CUDA context in the \p *device parameter. For the OpenGL interface, + * \p device must be NULL. An OpenGL context must be current when calling all + * NvEncodeAPI functions. If the creation of encoder session fails, the client + * must call ::NvEncDestroyEncoder API before exiting. + * + * \param [in] openSessionExParams + * Pointer to a ::NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS structure. + * \param [out] encoder + * Encode Session pointer to the NvEncodeAPI interface. + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_NO_ENCODE_DEVICE \n + * ::NV_ENC_ERR_UNSUPPORTED_DEVICE \n + * ::NV_ENC_ERR_INVALID_DEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncOpenEncodeSessionEx( + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS* openSessionExParams, void** encoder); + +// NvEncRegisterResource +/** + * \brief Registers a resource with the Nvidia Video Encoder Interface. + * + * Registers a resource with the Nvidia Video Encoder Interface for book + * keeping. The client is expected to pass the registered resource handle as + * well, while calling ::NvEncMapInputResource API. + * + * \param [in] encoder + * Pointer to the NVEncodeAPI interface. + * + * \param [in] registerResParams + * Pointer to a ::_NV_ENC_REGISTER_RESOURCE structure + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_RESOURCE_REGISTER_FAILED \n + * ::NV_ENC_ERR_GENERIC \n + * ::NV_ENC_ERR_UNIMPLEMENTED \n + * + */ +NVENCSTATUS NVENCAPI NvEncRegisterResource( + void* encoder, NV_ENC_REGISTER_RESOURCE* registerResParams); + +// NvEncUnregisterResource +/** + * \brief Unregisters a resource previously registered with the Nvidia Video + * Encoder Interface. + * + * Unregisters a resource previously registered with the Nvidia Video Encoder + * Interface. The client is expected to unregister any resource that it has + * registered with the Nvidia Video Encoder Interface before destroying the + * resource. + * + * \param [in] encoder + * Pointer to the NVEncodeAPI interface. + * + * \param [in] registeredResource + * The registered resource pointer that was returned in + * ::NvEncRegisterResource. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_RESOURCE_NOT_REGISTERED \n + * ::NV_ENC_ERR_GENERIC \n + * ::NV_ENC_ERR_UNIMPLEMENTED \n + * + */ +NVENCSTATUS NVENCAPI NvEncUnregisterResource( + void* encoder, NV_ENC_REGISTERED_PTR registeredResource); + +// NvEncReconfigureEncoder +/** + * \brief Reconfigure an existing encoding session. + * + * Reconfigure an existing encoding session. + * The client should call this API to change/reconfigure the parameter passed + * during NvEncInitializeEncoder API call. Currently Reconfiguration of + * following are not supported. Change in GOP structure. Change in sync-Async + * mode. Change in MaxWidth & MaxHeight. Change in PTD mode. + * + * Resolution change is possible only if maxEncodeWidth & maxEncodeHeight of + * NV_ENC_INITIALIZE_PARAMS is set while creating encoder session. + * + * \param [in] encoder + * Pointer to the NVEncodeAPI interface. + * + * \param [in] reInitEncodeParams + * Pointer to a ::NV_ENC_RECONFIGURE_PARAMS structure. + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_NO_ENCODE_DEVICE \n + * ::NV_ENC_ERR_UNSUPPORTED_DEVICE \n + * ::NV_ENC_ERR_INVALID_DEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncReconfigureEncoder( + void* encoder, NV_ENC_RECONFIGURE_PARAMS* reInitEncodeParams); + +// NvEncCreateMVBuffer +/** + * \brief Allocates output MV buffer for ME only mode. + * + * This function is used to allocate an output MV buffer. The size of the + * mvBuffer is dependent on the frame height and width of the last + * ::NvEncCreateInputBuffer() call. The NV_ENC_OUTPUT_PTR returned by the + * NvEncodeAPI interface in the + * ::NV_ENC_CREATE_MV_BUFFER::mvBuffer field should be used in + * ::NvEncRunMotionEstimationOnly() API. + * Client must lock ::NV_ENC_CREATE_MV_BUFFER::mvBuffer using + * ::NvEncLockBitstream() API to get the motion vector data. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] createMVBufferParams + * Pointer to the ::NV_ENC_CREATE_MV_BUFFER structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI NvEncCreateMVBuffer( + void* encoder, NV_ENC_CREATE_MV_BUFFER* createMVBufferParams); + +// NvEncDestroyMVBuffer +/** + * \brief Release an output MV buffer for ME only mode. + * + * This function is used to release the output MV buffer allocated using + * the ::NvEncCreateMVBuffer() function. The client must release the output + * mvBuffer using this function before destroying the encoder session. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] mvBuffer + * Pointer to the mvBuffer being released. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI NvEncDestroyMVBuffer(void* encoder, + NV_ENC_OUTPUT_PTR mvBuffer); + +// NvEncRunMotionEstimationOnly +/** + * \brief Submit an input picture and reference frame for motion estimation in + * ME only mode. + * + * This function is used to submit the input frame and reference frame for + * motion estimation. The ME parameters are passed using *meOnlyParams which is + * a pointer to ::_NV_ENC_MEONLY_PARAMS structure. Client must lock + * ::NV_ENC_CREATE_MV_BUFFER::mvBuffer using ::NvEncLockBitstream() API to get + * the motion vector data. to get motion vector data. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] meOnlyParams + * Pointer to the ::_NV_ENC_MEONLY_PARAMS structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_NEED_MORE_INPUT \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI +NvEncRunMotionEstimationOnly(void* encoder, NV_ENC_MEONLY_PARAMS* meOnlyParams); + +// NvEncodeAPIGetMaxSupportedVersion +/** + * \brief Get the largest NvEncodeAPI version supported by the driver. + * + * This function can be used by clients to determine if the driver supports + * the NvEncodeAPI header the application was compiled with. + * + * \param [out] version + * Pointer to the requested value. The 4 least significant bits in the + * returned indicate the minor version and the rest of the bits indicate the + * major version of the largest supported version. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + */ +NVENCSTATUS NVENCAPI NvEncodeAPIGetMaxSupportedVersion(uint32_t* version); + +// NvEncGetLastErrorString +/** + * \brief Get the description of the last error reported by the API. + * + * This function returns a null-terminated string that can be used by clients to + * better understand the reason for failure of a previous API call. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * + * \return + * Pointer to buffer containing the details of the last error encountered by + * the API. + */ +const char* NVENCAPI NvEncGetLastErrorString(void* encoder); + +/// \cond API PFN +/* + * Defines API function pointers + */ +typedef NVENCSTATUS(NVENCAPI* PNVENCOPENENCODESESSION)(void* device, + uint32_t deviceType, + void** encoder); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEGUIDCOUNT)( + void* encoder, uint32_t* encodeGUIDCount); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEGUIDS)(void* encoder, GUID* GUIDs, + uint32_t guidArraySize, + uint32_t* GUIDCount); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPROFILEGUIDCOUNT)( + void* encoder, GUID encodeGUID, uint32_t* encodeProfileGUIDCount); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPROFILEGUIDS)( + void* encoder, GUID encodeGUID, GUID* profileGUIDs, uint32_t guidArraySize, + uint32_t* GUIDCount); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETINPUTFORMATCOUNT)( + void* encoder, GUID encodeGUID, uint32_t* inputFmtCount); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETINPUTFORMATS)( + void* encoder, GUID encodeGUID, NV_ENC_BUFFER_FORMAT* inputFmts, + uint32_t inputFmtArraySize, uint32_t* inputFmtCount); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODECAPS)(void* encoder, + GUID encodeGUID, + NV_ENC_CAPS_PARAM* capsParam, + int* capsVal); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPRESETCOUNT)( + void* encoder, GUID encodeGUID, uint32_t* encodePresetGUIDCount); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPRESETGUIDS)( + void* encoder, GUID encodeGUID, GUID* presetGUIDs, uint32_t guidArraySize, + uint32_t* encodePresetGUIDCount); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPRESETCONFIG)( + void* encoder, GUID encodeGUID, GUID presetGUID, + NV_ENC_PRESET_CONFIG* presetConfig); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPRESETCONFIGEX)( + void* encoder, GUID encodeGUID, GUID presetGUID, + NV_ENC_TUNING_INFO tuningInfo, NV_ENC_PRESET_CONFIG* presetConfig); +typedef NVENCSTATUS(NVENCAPI* PNVENCINITIALIZEENCODER)( + void* encoder, NV_ENC_INITIALIZE_PARAMS* createEncodeParams); +typedef NVENCSTATUS(NVENCAPI* PNVENCCREATEINPUTBUFFER)( + void* encoder, NV_ENC_CREATE_INPUT_BUFFER* createInputBufferParams); +typedef NVENCSTATUS(NVENCAPI* PNVENCDESTROYINPUTBUFFER)( + void* encoder, NV_ENC_INPUT_PTR inputBuffer); +typedef NVENCSTATUS(NVENCAPI* PNVENCCREATEBITSTREAMBUFFER)( + void* encoder, NV_ENC_CREATE_BITSTREAM_BUFFER* createBitstreamBufferParams); +typedef NVENCSTATUS(NVENCAPI* PNVENCDESTROYBITSTREAMBUFFER)( + void* encoder, NV_ENC_OUTPUT_PTR bitstreamBuffer); +typedef NVENCSTATUS(NVENCAPI* PNVENCENCODEPICTURE)( + void* encoder, NV_ENC_PIC_PARAMS* encodePicParams); +typedef NVENCSTATUS(NVENCAPI* PNVENCLOCKBITSTREAM)( + void* encoder, NV_ENC_LOCK_BITSTREAM* lockBitstreamBufferParams); +typedef NVENCSTATUS(NVENCAPI* PNVENCUNLOCKBITSTREAM)( + void* encoder, NV_ENC_OUTPUT_PTR bitstreamBuffer); +typedef NVENCSTATUS(NVENCAPI* PNVENCLOCKINPUTBUFFER)( + void* encoder, NV_ENC_LOCK_INPUT_BUFFER* lockInputBufferParams); +typedef NVENCSTATUS(NVENCAPI* PNVENCUNLOCKINPUTBUFFER)( + void* encoder, NV_ENC_INPUT_PTR inputBuffer); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODESTATS)(void* encoder, + NV_ENC_STAT* encodeStats); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETSEQUENCEPARAMS)( + void* encoder, NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload); +typedef NVENCSTATUS(NVENCAPI* PNVENCREGISTERASYNCEVENT)( + void* encoder, NV_ENC_EVENT_PARAMS* eventParams); +typedef NVENCSTATUS(NVENCAPI* PNVENCUNREGISTERASYNCEVENT)( + void* encoder, NV_ENC_EVENT_PARAMS* eventParams); +typedef NVENCSTATUS(NVENCAPI* PNVENCMAPINPUTRESOURCE)( + void* encoder, NV_ENC_MAP_INPUT_RESOURCE* mapInputResParams); +typedef NVENCSTATUS(NVENCAPI* PNVENCUNMAPINPUTRESOURCE)( + void* encoder, NV_ENC_INPUT_PTR mappedInputBuffer); +typedef NVENCSTATUS(NVENCAPI* PNVENCDESTROYENCODER)(void* encoder); +typedef NVENCSTATUS(NVENCAPI* PNVENCINVALIDATEREFFRAMES)( + void* encoder, uint64_t invalidRefFrameTimeStamp); +typedef NVENCSTATUS(NVENCAPI* PNVENCOPENENCODESESSIONEX)( + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS* openSessionExParams, void** encoder); +typedef NVENCSTATUS(NVENCAPI* PNVENCREGISTERRESOURCE)( + void* encoder, NV_ENC_REGISTER_RESOURCE* registerResParams); +typedef NVENCSTATUS(NVENCAPI* PNVENCUNREGISTERRESOURCE)( + void* encoder, NV_ENC_REGISTERED_PTR registeredRes); +typedef NVENCSTATUS(NVENCAPI* PNVENCRECONFIGUREENCODER)( + void* encoder, NV_ENC_RECONFIGURE_PARAMS* reInitEncodeParams); + +typedef NVENCSTATUS(NVENCAPI* PNVENCCREATEMVBUFFER)( + void* encoder, NV_ENC_CREATE_MV_BUFFER* createMVBufferParams); +typedef NVENCSTATUS(NVENCAPI* PNVENCDESTROYMVBUFFER)( + void* encoder, NV_ENC_OUTPUT_PTR mvBuffer); +typedef NVENCSTATUS(NVENCAPI* PNVENCRUNMOTIONESTIMATIONONLY)( + void* encoder, NV_ENC_MEONLY_PARAMS* meOnlyParams); +typedef const char*(NVENCAPI* PNVENCGETLASTERROR)(void* encoder); +typedef NVENCSTATUS(NVENCAPI* PNVENCSETIOCUDASTREAMS)( + void* encoder, NV_ENC_CUSTREAM_PTR inputStream, + NV_ENC_CUSTREAM_PTR outputStream); +typedef NVENCSTATUS(NVENCAPI* PNVENCGETSEQUENCEPARAMEX)( + void* encoder, NV_ENC_INITIALIZE_PARAMS* encInitParams, + NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload); + +/// \endcond + +/** @} */ /* END ENCODE_FUNC */ + +/** + * \ingroup ENCODER_STRUCTURE + * NV_ENCODE_API_FUNCTION_LIST + */ +typedef struct _NV_ENCODE_API_FUNCTION_LIST { + uint32_t + version; /**< [in]: Client should pass NV_ENCODE_API_FUNCTION_LIST_VER. */ + uint32_t reserved; /**< [in]: Reserved and should be set to 0. */ + PNVENCOPENENCODESESSION + nvEncOpenEncodeSession; /**< [out]: Client should access + ::NvEncOpenEncodeSession() API through this + pointer. */ + PNVENCGETENCODEGUIDCOUNT + nvEncGetEncodeGUIDCount; /**< [out]: Client should access + ::NvEncGetEncodeGUIDCount() API through this + pointer. */ + PNVENCGETENCODEPRESETCOUNT + nvEncGetEncodeProfileGUIDCount; /**< [out]: Client should access + ::NvEncGetEncodeProfileGUIDCount() API + through this pointer.*/ + PNVENCGETENCODEPRESETGUIDS + nvEncGetEncodeProfileGUIDs; /**< [out]: Client should access + ::NvEncGetEncodeProfileGUIDs() API through + this pointer. */ + PNVENCGETENCODEGUIDS nvEncGetEncodeGUIDs; /**< [out]: Client should access + ::NvEncGetEncodeGUIDs() API + through this pointer. */ + PNVENCGETINPUTFORMATCOUNT + nvEncGetInputFormatCount; /**< [out]: Client should access + ::NvEncGetInputFormatCount() API through this + pointer. */ + PNVENCGETINPUTFORMATS nvEncGetInputFormats; /**< [out]: Client should access + ::NvEncGetInputFormats() API + through this pointer. */ + PNVENCGETENCODECAPS + nvEncGetEncodeCaps; /**< [out]: Client should access + ::NvEncGetEncodeCaps() API through this pointer. */ + PNVENCGETENCODEPRESETCOUNT + nvEncGetEncodePresetCount; /**< [out]: Client should access + ::NvEncGetEncodePresetCount() API through + this pointer. */ + PNVENCGETENCODEPRESETGUIDS + nvEncGetEncodePresetGUIDs; /**< [out]: Client should access + ::NvEncGetEncodePresetGUIDs() API through + this pointer. */ + PNVENCGETENCODEPRESETCONFIG + nvEncGetEncodePresetConfig; /**< [out]: Client should access + ::NvEncGetEncodePresetConfig() API through + this pointer. */ + PNVENCINITIALIZEENCODER + nvEncInitializeEncoder; /**< [out]: Client should access + ::NvEncInitializeEncoder() API through this + pointer. */ + PNVENCCREATEINPUTBUFFER + nvEncCreateInputBuffer; /**< [out]: Client should access + ::NvEncCreateInputBuffer() API through this + pointer. */ + PNVENCDESTROYINPUTBUFFER + nvEncDestroyInputBuffer; /**< [out]: Client should access + ::NvEncDestroyInputBuffer() API through this + pointer. */ + PNVENCCREATEBITSTREAMBUFFER + nvEncCreateBitstreamBuffer; /**< [out]: Client should access + ::NvEncCreateBitstreamBuffer() API through + this pointer. */ + PNVENCDESTROYBITSTREAMBUFFER + nvEncDestroyBitstreamBuffer; /**< [out]: Client should access + ::NvEncDestroyBitstreamBuffer() API + through this pointer. */ + PNVENCENCODEPICTURE + nvEncEncodePicture; /**< [out]: Client should access + ::NvEncEncodePicture() API through this pointer. */ + PNVENCLOCKBITSTREAM + nvEncLockBitstream; /**< [out]: Client should access + ::NvEncLockBitstream() API through this pointer. */ + PNVENCUNLOCKBITSTREAM nvEncUnlockBitstream; /**< [out]: Client should access + ::NvEncUnlockBitstream() API + through this pointer. */ + PNVENCLOCKINPUTBUFFER nvEncLockInputBuffer; /**< [out]: Client should access + ::NvEncLockInputBuffer() API + through this pointer. */ + PNVENCUNLOCKINPUTBUFFER + nvEncUnlockInputBuffer; /**< [out]: Client should access + ::NvEncUnlockInputBuffer() API through this + pointer. */ + PNVENCGETENCODESTATS nvEncGetEncodeStats; /**< [out]: Client should access + ::NvEncGetEncodeStats() API + through this pointer. */ + PNVENCGETSEQUENCEPARAMS + nvEncGetSequenceParams; /**< [out]: Client should access + ::NvEncGetSequenceParams() API through this + pointer. */ + PNVENCREGISTERASYNCEVENT + nvEncRegisterAsyncEvent; /**< [out]: Client should access + ::NvEncRegisterAsyncEvent() API through this + pointer. */ + PNVENCUNREGISTERASYNCEVENT + nvEncUnregisterAsyncEvent; /**< [out]: Client should access + ::NvEncUnregisterAsyncEvent() API through + this pointer. */ + PNVENCMAPINPUTRESOURCE nvEncMapInputResource; /**< [out]: Client should access + ::NvEncMapInputResource() API + through this pointer. */ + PNVENCUNMAPINPUTRESOURCE + nvEncUnmapInputResource; /**< [out]: Client should access + ::NvEncUnmapInputResource() API through this + pointer. */ + PNVENCDESTROYENCODER nvEncDestroyEncoder; /**< [out]: Client should access + ::NvEncDestroyEncoder() API + through this pointer. */ + PNVENCINVALIDATEREFFRAMES + nvEncInvalidateRefFrames; /**< [out]: Client should access + ::NvEncInvalidateRefFrames() API through this + pointer. */ + PNVENCOPENENCODESESSIONEX + nvEncOpenEncodeSessionEx; /**< [out]: Client should access + ::NvEncOpenEncodeSession() API through this + pointer. */ + PNVENCREGISTERRESOURCE nvEncRegisterResource; /**< [out]: Client should access + ::NvEncRegisterResource() API + through this pointer. */ + PNVENCUNREGISTERRESOURCE + nvEncUnregisterResource; /**< [out]: Client should access + ::NvEncUnregisterResource() API through this + pointer. */ + PNVENCRECONFIGUREENCODER + nvEncReconfigureEncoder; /**< [out]: Client should access + ::NvEncReconfigureEncoder() API through this + pointer. */ + void* reserved1; + PNVENCCREATEMVBUFFER + nvEncCreateMVBuffer; /**< [out]: Client should access + ::NvEncCreateMVBuffer API through this pointer. */ + PNVENCDESTROYMVBUFFER nvEncDestroyMVBuffer; /**< [out]: Client should access + ::NvEncDestroyMVBuffer API + through this pointer. */ + PNVENCRUNMOTIONESTIMATIONONLY + nvEncRunMotionEstimationOnly; /**< [out]: Client should access + ::NvEncRunMotionEstimationOnly API + through this pointer. */ + PNVENCGETLASTERROR nvEncGetLastErrorString; /**< [out]: Client should access + ::nvEncGetLastErrorString API + through this pointer. */ + PNVENCSETIOCUDASTREAMS nvEncSetIOCudaStreams; /**< [out]: Client should access + ::nvEncSetIOCudaStreams API + through this pointer. */ + PNVENCGETENCODEPRESETCONFIGEX + nvEncGetEncodePresetConfigEx; /**< [out]: Client should access + ::NvEncGetEncodePresetConfigEx() API + through this pointer. */ + PNVENCGETSEQUENCEPARAMEX + nvEncGetSequenceParamEx; /**< [out]: Client should access + ::NvEncGetSequenceParamEx() API through this + pointer. */ + void* reserved2[277]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENCODE_API_FUNCTION_LIST; + +/** Macro for constructing the version field of ::_NV_ENCODEAPI_FUNCTION_LIST. + */ +#define NV_ENCODE_API_FUNCTION_LIST_VER NVENCAPI_STRUCT_VERSION(2) + +// NvEncodeAPICreateInstance +/** + * \ingroup ENCODE_FUNC + * Entry Point to the NvEncodeAPI interface. + * + * Creates an instance of the NvEncodeAPI interface, and populates the + * pFunctionList with function pointers to the API routines implemented by the + * NvEncodeAPI interface. + * + * \param [out] functionList + * + * \return + * ::NV_ENC_SUCCESS + * ::NV_ENC_ERR_INVALID_PTR + */ +NVENCSTATUS NVENCAPI +NvEncodeAPICreateInstance(NV_ENCODE_API_FUNCTION_LIST* functionList); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/nvcodec/Interface/nvcuvid.h b/thirdparty/nvcodec/Interface/nvcuvid.h new file mode 100644 index 0000000..dea5e7f --- /dev/null +++ b/thirdparty/nvcodec/Interface/nvcuvid.h @@ -0,0 +1,436 @@ +/* + * This copyright notice applies to this header file only: + * + * Copyright (c) 2010-2020 NVIDIA Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the software, and to permit persons to whom the + * software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/********************************************************************************************************************/ +//! \file nvcuvid.h +//! NVDECODE API provides video decoding interface to NVIDIA GPU devices. +//! \date 2015-2020 +//! This file contains the interface constants, structure definitions and function prototypes. +/********************************************************************************************************************/ + +#if !defined(__NVCUVID_H__) +#define __NVCUVID_H__ + +#include "cuviddec.h" + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ + + +/***********************************************/ +//! +//! High-level helper APIs for video sources +//! +/***********************************************/ + +typedef void *CUvideosource; +typedef void *CUvideoparser; +typedef long long CUvideotimestamp; + + +/************************************************************************/ +//! \enum cudaVideoState +//! Video source state enums +//! Used in cuvidSetVideoSourceState and cuvidGetVideoSourceState APIs +/************************************************************************/ +typedef enum { + cudaVideoState_Error = -1, /**< Error state (invalid source) */ + cudaVideoState_Stopped = 0, /**< Source is stopped (or reached end-of-stream) */ + cudaVideoState_Started = 1 /**< Source is running and delivering data */ +} cudaVideoState; + +/************************************************************************/ +//! \enum cudaAudioCodec +//! Audio compression enums +//! Used in CUAUDIOFORMAT structure +/************************************************************************/ +typedef enum { + cudaAudioCodec_MPEG1=0, /**< MPEG-1 Audio */ + cudaAudioCodec_MPEG2, /**< MPEG-2 Audio */ + cudaAudioCodec_MP3, /**< MPEG-1 Layer III Audio */ + cudaAudioCodec_AC3, /**< Dolby Digital (AC3) Audio */ + cudaAudioCodec_LPCM, /**< PCM Audio */ + cudaAudioCodec_AAC, /**< AAC Audio */ +} cudaAudioCodec; + +/************************************************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDEOFORMAT +//! Video format +//! Used in cuvidGetSourceVideoFormat API +/************************************************************************************************/ +typedef struct +{ + cudaVideoCodec codec; /**< OUT: Compression format */ + /** + * OUT: frame rate = numerator / denominator (for example: 30000/1001) + */ + struct { + /**< OUT: frame rate numerator (0 = unspecified or variable frame rate) */ + unsigned int numerator; + /**< OUT: frame rate denominator (0 = unspecified or variable frame rate) */ + unsigned int denominator; + } frame_rate; + unsigned char progressive_sequence; /**< OUT: 0=interlaced, 1=progressive */ + unsigned char bit_depth_luma_minus8; /**< OUT: high bit depth luma. E.g, 2 for 10-bitdepth, 4 for 12-bitdepth */ + unsigned char bit_depth_chroma_minus8; /**< OUT: high bit depth chroma. E.g, 2 for 10-bitdepth, 4 for 12-bitdepth */ + unsigned char min_num_decode_surfaces; /**< OUT: Minimum number of decode surfaces to be allocated for correct + decoding. The client can send this value in ulNumDecodeSurfaces + (in CUVIDDECODECREATEINFO structure). + This guarantees correct functionality and optimal video memory + usage but not necessarily the best performance, which depends on + the design of the overall application. The optimal number of + decode surfaces (in terms of performance and memory utilization) + should be decided by experimentation for each application, but it + cannot go below min_num_decode_surfaces. + If this value is used for ulNumDecodeSurfaces then it must be + returned to parser during sequence callback. */ + unsigned int coded_width; /**< OUT: coded frame width in pixels */ + unsigned int coded_height; /**< OUT: coded frame height in pixels */ + /** + * area of the frame that should be displayed + * typical example: + * coded_width = 1920, coded_height = 1088 + * display_area = { 0,0,1920,1080 } + */ + struct { + int left; /**< OUT: left position of display rect */ + int top; /**< OUT: top position of display rect */ + int right; /**< OUT: right position of display rect */ + int bottom; /**< OUT: bottom position of display rect */ + } display_area; + cudaVideoChromaFormat chroma_format; /**< OUT: Chroma format */ + unsigned int bitrate; /**< OUT: video bitrate (bps, 0=unknown) */ + /** + * OUT: Display Aspect Ratio = x:y (4:3, 16:9, etc) + */ + struct { + int x; + int y; + } display_aspect_ratio; + /** + * Video Signal Description + * Refer section E.2.1 (VUI parameters semantics) of H264 spec file + */ + struct { + unsigned char video_format : 3; /**< OUT: 0-Component, 1-PAL, 2-NTSC, 3-SECAM, 4-MAC, 5-Unspecified */ + unsigned char video_full_range_flag : 1; /**< OUT: indicates the black level and luma and chroma range */ + unsigned char reserved_zero_bits : 4; /**< Reserved bits */ + unsigned char color_primaries; /**< OUT: chromaticity coordinates of source primaries */ + unsigned char transfer_characteristics; /**< OUT: opto-electronic transfer characteristic of the source picture */ + unsigned char matrix_coefficients; /**< OUT: used in deriving luma and chroma signals from RGB primaries */ + } video_signal_description; + unsigned int seqhdr_data_length; /**< OUT: Additional bytes following (CUVIDEOFORMATEX) */ +} CUVIDEOFORMAT; + +/****************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDOPERATINGPOINTINFO +//! Operating point information of scalable bitstream +/****************************************************************/ +typedef struct +{ + cudaVideoCodec codec; + union + { + struct + { + unsigned char operating_points_cnt; + unsigned char reserved24_bits[3]; + unsigned short operating_points_idc[32]; + } av1; + unsigned char CodecReserved[1024]; + }; +} CUVIDOPERATINGPOINTINFO; + +/****************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDAV1SEQHDR +//! AV1 specific sequence header information +/****************************************************************/ +typedef struct { + unsigned int max_width; + unsigned int max_height; + unsigned char reserved[1016]; +} CUVIDAV1SEQHDR; + +/****************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDEOFORMATEX +//! Video format including raw sequence header information +//! Used in cuvidGetSourceVideoFormat API +/****************************************************************/ +typedef struct +{ + CUVIDEOFORMAT format; /**< OUT: CUVIDEOFORMAT structure */ + union { + CUVIDAV1SEQHDR av1; + unsigned char raw_seqhdr_data[1024]; /**< OUT: Sequence header data */ + }; +} CUVIDEOFORMATEX; + +/****************************************************************/ +//! \ingroup STRUCTS +//! \struct CUAUDIOFORMAT +//! Audio formats +//! Used in cuvidGetSourceAudioFormat API +/****************************************************************/ +typedef struct +{ + cudaAudioCodec codec; /**< OUT: Compression format */ + unsigned int channels; /**< OUT: number of audio channels */ + unsigned int samplespersec; /**< OUT: sampling frequency */ + unsigned int bitrate; /**< OUT: For uncompressed, can also be used to determine bits per sample */ + unsigned int reserved1; /**< Reserved for future use */ + unsigned int reserved2; /**< Reserved for future use */ +} CUAUDIOFORMAT; + + +/***************************************************************/ +//! \enum CUvideopacketflags +//! Data packet flags +//! Used in CUVIDSOURCEDATAPACKET structure +/***************************************************************/ +typedef enum { + CUVID_PKT_ENDOFSTREAM = 0x01, /**< Set when this is the last packet for this stream */ + CUVID_PKT_TIMESTAMP = 0x02, /**< Timestamp is valid */ + CUVID_PKT_DISCONTINUITY = 0x04, /**< Set when a discontinuity has to be signalled */ + CUVID_PKT_ENDOFPICTURE = 0x08, /**< Set when the packet contains exactly one frame or one field */ + CUVID_PKT_NOTIFY_EOS = 0x10, /**< If this flag is set along with CUVID_PKT_ENDOFSTREAM, an additional (dummy) + display callback will be invoked with null value of CUVIDPARSERDISPINFO which + should be interpreted as end of the stream. */ +} CUvideopacketflags; + +/*****************************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDSOURCEDATAPACKET +//! Data Packet +//! Used in cuvidParseVideoData API +//! IN for cuvidParseVideoData +/*****************************************************************************/ +typedef struct _CUVIDSOURCEDATAPACKET +{ + unsigned long flags; /**< IN: Combination of CUVID_PKT_XXX flags */ + unsigned long payload_size; /**< IN: number of bytes in the payload (may be zero if EOS flag is set) */ + const unsigned char *payload; /**< IN: Pointer to packet payload data (may be NULL if EOS flag is set) */ + CUvideotimestamp timestamp; /**< IN: Presentation time stamp (10MHz clock), only valid if + CUVID_PKT_TIMESTAMP flag is set */ +} CUVIDSOURCEDATAPACKET; + +// Callback for packet delivery +typedef int (CUDAAPI *PFNVIDSOURCECALLBACK)(void *, CUVIDSOURCEDATAPACKET *); + +/**************************************************************************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDSOURCEPARAMS +//! Describes parameters needed in cuvidCreateVideoSource API +//! NVDECODE API is intended for HW accelerated video decoding so CUvideosource doesn't have audio demuxer for all supported +//! containers. It's recommended to clients to use their own or third party demuxer if audio support is needed. +/**************************************************************************************************************************/ +typedef struct _CUVIDSOURCEPARAMS +{ + unsigned int ulClockRate; /**< IN: Time stamp units in Hz (0=default=10000000Hz) */ + unsigned int bAnnexb : 1; /**< IN: AV1 annexB stream */ + unsigned int uReserved : 31; /**< Reserved for future use - set to zero */ + unsigned int uReserved1[6]; /**< Reserved for future use - set to zero */ + void *pUserData; /**< IN: User private data passed in to the data handlers */ + PFNVIDSOURCECALLBACK pfnVideoDataHandler; /**< IN: Called to deliver video packets */ + PFNVIDSOURCECALLBACK pfnAudioDataHandler; /**< IN: Called to deliver audio packets. */ + void *pvReserved2[8]; /**< Reserved for future use - set to NULL */ +} CUVIDSOURCEPARAMS; + + +/**********************************************/ +//! \ingroup ENUMS +//! \enum CUvideosourceformat_flags +//! CUvideosourceformat_flags +//! Used in cuvidGetSourceVideoFormat API +/**********************************************/ +typedef enum { + CUVID_FMT_EXTFORMATINFO = 0x100 /**< Return extended format structure (CUVIDEOFORMATEX) */ +} CUvideosourceformat_flags; + +#if !defined(__APPLE__) +/***************************************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidCreateVideoSource(CUvideosource *pObj, const char *pszFileName, CUVIDSOURCEPARAMS *pParams) +//! Create CUvideosource object. CUvideosource spawns demultiplexer thread that provides two callbacks: +//! pfnVideoDataHandler() and pfnAudioDataHandler() +//! NVDECODE API is intended for HW accelerated video decoding so CUvideosource doesn't have audio demuxer for all supported +//! containers. It's recommended to clients to use their own or third party demuxer if audio support is needed. +/***************************************************************************************************************************/ +CUresult CUDAAPI cuvidCreateVideoSource(CUvideosource *pObj, const char *pszFileName, CUVIDSOURCEPARAMS *pParams); + +/***************************************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidCreateVideoSourceW(CUvideosource *pObj, const wchar_t *pwszFileName, CUVIDSOURCEPARAMS *pParams) +//! Create video source +/***************************************************************************************************************************/ +CUresult CUDAAPI cuvidCreateVideoSourceW(CUvideosource *pObj, const wchar_t *pwszFileName, CUVIDSOURCEPARAMS *pParams); + +/********************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidDestroyVideoSource(CUvideosource obj) +//! Destroy video source +/********************************************************************/ +CUresult CUDAAPI cuvidDestroyVideoSource(CUvideosource obj); + +/******************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidSetVideoSourceState(CUvideosource obj, cudaVideoState state) +//! Set video source state to: +//! cudaVideoState_Started - to signal the source to run and deliver data +//! cudaVideoState_Stopped - to stop the source from delivering the data +//! cudaVideoState_Error - invalid source +/******************************************************************************************/ +CUresult CUDAAPI cuvidSetVideoSourceState(CUvideosource obj, cudaVideoState state); + +/******************************************************************************************/ +//! \ingroup FUNCTS +//! \fn cudaVideoState CUDAAPI cuvidGetVideoSourceState(CUvideosource obj) +//! Get video source state +//! Returns: +//! cudaVideoState_Started - if Source is running and delivering data +//! cudaVideoState_Stopped - if Source is stopped or reached end-of-stream +//! cudaVideoState_Error - if Source is in error state +/******************************************************************************************/ +cudaVideoState CUDAAPI cuvidGetVideoSourceState(CUvideosource obj); + +/******************************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidGetSourceVideoFormat(CUvideosource obj, CUVIDEOFORMAT *pvidfmt, unsigned int flags) +//! Gets video source format in pvidfmt, flags is set to combination of CUvideosourceformat_flags as per requirement +/******************************************************************************************************************/ +CUresult CUDAAPI cuvidGetSourceVideoFormat(CUvideosource obj, CUVIDEOFORMAT *pvidfmt, unsigned int flags); + +/**************************************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidGetSourceAudioFormat(CUvideosource obj, CUAUDIOFORMAT *paudfmt, unsigned int flags) +//! Get audio source format +//! NVDECODE API is intended for HW accelerated video decoding so CUvideosource doesn't have audio demuxer for all supported +//! containers. It's recommended to clients to use their own or third party demuxer if audio support is needed. +/**************************************************************************************************************************/ +CUresult CUDAAPI cuvidGetSourceAudioFormat(CUvideosource obj, CUAUDIOFORMAT *paudfmt, unsigned int flags); + +#endif +/**********************************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDPARSERDISPINFO +//! Used in cuvidParseVideoData API with PFNVIDDISPLAYCALLBACK pfnDisplayPicture +/**********************************************************************************/ +typedef struct _CUVIDPARSERDISPINFO +{ + int picture_index; /**< OUT: Index of the current picture */ + int progressive_frame; /**< OUT: 1 if progressive frame; 0 otherwise */ + int top_field_first; /**< OUT: 1 if top field is displayed first; 0 otherwise */ + int repeat_first_field; /**< OUT: Number of additional fields (1=ivtc, 2=frame doubling, 4=frame tripling, + -1=unpaired field) */ + CUvideotimestamp timestamp; /**< OUT: Presentation time stamp */ +} CUVIDPARSERDISPINFO; + +/***********************************************************************************************************************/ +//! Parser callbacks +//! The parser will call these synchronously from within cuvidParseVideoData(), whenever there is sequence change or a picture +//! is ready to be decoded and/or displayed. First argument in functions is "void *pUserData" member of structure CUVIDSOURCEPARAMS +//! Return values from these callbacks are interpreted as below. If the callbacks return failure, it will be propagated by +//! cuvidParseVideoData() to the application. +//! Parser picks default operating point as 0 and outputAllLayers flag as 0 if PFNVIDOPPOINTCALLBACK is not set or return value is +//! -1 or invalid operating point. +//! PFNVIDSEQUENCECALLBACK : 0: fail, 1: succeeded, > 1: override dpb size of parser (set by CUVIDPARSERPARAMS::ulMaxNumDecodeSurfaces +//! while creating parser) +//! PFNVIDDECODECALLBACK : 0: fail, >=1: succeeded +//! PFNVIDDISPLAYCALLBACK : 0: fail, >=1: succeeded +//! PFNVIDOPPOINTCALLBACK : <0: fail, >=0: succeeded (bit 0-9: OperatingPoint, bit 10-10: outputAllLayers, bit 11-30: reserved) +/***********************************************************************************************************************/ +typedef int (CUDAAPI *PFNVIDSEQUENCECALLBACK)(void *, CUVIDEOFORMAT *); +typedef int (CUDAAPI *PFNVIDDECODECALLBACK)(void *, CUVIDPICPARAMS *); +typedef int (CUDAAPI *PFNVIDDISPLAYCALLBACK)(void *, CUVIDPARSERDISPINFO *); +typedef int (CUDAAPI *PFNVIDOPPOINTCALLBACK)(void *, CUVIDOPERATINGPOINTINFO*); + +/**************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDPARSERPARAMS +//! Used in cuvidCreateVideoParser API +/**************************************/ +typedef struct _CUVIDPARSERPARAMS +{ + cudaVideoCodec CodecType; /**< IN: cudaVideoCodec_XXX */ + unsigned int ulMaxNumDecodeSurfaces; /**< IN: Max # of decode surfaces (parser will cycle through these) */ + unsigned int ulClockRate; /**< IN: Timestamp units in Hz (0=default=10000000Hz) */ + unsigned int ulErrorThreshold; /**< IN: % Error threshold (0-100) for calling pfnDecodePicture (100=always + IN: call pfnDecodePicture even if picture bitstream is fully corrupted) */ + unsigned int ulMaxDisplayDelay; /**< IN: Max display queue delay (improves pipelining of decode with display) + 0=no delay (recommended values: 2..4) */ + unsigned int bAnnexb : 1; /**< IN: AV1 annexB stream */ + unsigned int uReserved : 31; /**< Reserved for future use - set to zero */ + unsigned int uReserved1[4]; /**< IN: Reserved for future use - set to 0 */ + void *pUserData; /**< IN: User data for callbacks */ + PFNVIDSEQUENCECALLBACK pfnSequenceCallback; /**< IN: Called before decoding frames and/or whenever there is a fmt change */ + PFNVIDDECODECALLBACK pfnDecodePicture; /**< IN: Called when a picture is ready to be decoded (decode order) */ + PFNVIDDISPLAYCALLBACK pfnDisplayPicture; /**< IN: Called whenever a picture is ready to be displayed (display order) */ + PFNVIDOPPOINTCALLBACK pfnGetOperatingPoint; /**< IN: Called from AV1 sequence header to get operating point of a AV1 + scalable bitstream */ + void *pvReserved2[6]; /**< Reserved for future use - set to NULL */ + CUVIDEOFORMATEX *pExtVideoInfo; /**< IN: [Optional] sequence header data from system layer */ +} CUVIDPARSERPARAMS; + +/************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidCreateVideoParser(CUvideoparser *pObj, CUVIDPARSERPARAMS *pParams) +//! Create video parser object and initialize +/************************************************************************************************/ +CUresult CUDAAPI cuvidCreateVideoParser(CUvideoparser *pObj, CUVIDPARSERPARAMS *pParams); + +/************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidParseVideoData(CUvideoparser obj, CUVIDSOURCEDATAPACKET *pPacket) +//! Parse the video data from source data packet in pPacket +//! Extracts parameter sets like SPS, PPS, bitstream etc. from pPacket and +//! calls back pfnDecodePicture with CUVIDPICPARAMS data for kicking of HW decoding +//! calls back pfnSequenceCallback with CUVIDEOFORMAT data for initial sequence header or when +//! the decoder encounters a video format change +//! calls back pfnDisplayPicture with CUVIDPARSERDISPINFO data to display a video frame +/************************************************************************************************/ +CUresult CUDAAPI cuvidParseVideoData(CUvideoparser obj, CUVIDSOURCEDATAPACKET *pPacket); + +/************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidDestroyVideoParser(CUvideoparser obj) +//! Destroy the video parser +/************************************************************************************************/ +CUresult CUDAAPI cuvidDestroyVideoParser(CUvideoparser obj); + +/**********************************************************************************************/ + +#if defined(__cplusplus) +} +#endif /* __cplusplus */ + +#endif // __NVCUVID_H__ + + diff --git a/thirdparty/nvcodec/Lib/Win32/nvcuvid.lib b/thirdparty/nvcodec/Lib/Win32/nvcuvid.lib new file mode 100644 index 0000000..0f6c15b Binary files /dev/null and b/thirdparty/nvcodec/Lib/Win32/nvcuvid.lib differ diff --git a/thirdparty/nvcodec/Lib/Win32/nvencodeapi.lib b/thirdparty/nvcodec/Lib/Win32/nvencodeapi.lib new file mode 100644 index 0000000..8dccc3b Binary files /dev/null and b/thirdparty/nvcodec/Lib/Win32/nvencodeapi.lib differ diff --git a/thirdparty/nvcodec/Lib/linux/stubs/aarch64/libnvcuvid.so b/thirdparty/nvcodec/Lib/linux/stubs/aarch64/libnvcuvid.so new file mode 100644 index 0000000..f0997c8 Binary files /dev/null and b/thirdparty/nvcodec/Lib/linux/stubs/aarch64/libnvcuvid.so differ diff --git a/thirdparty/nvcodec/Lib/linux/stubs/aarch64/libnvidia-encode.so b/thirdparty/nvcodec/Lib/linux/stubs/aarch64/libnvidia-encode.so new file mode 100644 index 0000000..f2898c5 Binary files /dev/null and b/thirdparty/nvcodec/Lib/linux/stubs/aarch64/libnvidia-encode.so differ diff --git a/thirdparty/nvcodec/Lib/linux/stubs/ppc64le/libnvcuvid.so b/thirdparty/nvcodec/Lib/linux/stubs/ppc64le/libnvcuvid.so new file mode 100644 index 0000000..44301ef Binary files /dev/null and b/thirdparty/nvcodec/Lib/linux/stubs/ppc64le/libnvcuvid.so differ diff --git a/thirdparty/nvcodec/Lib/linux/stubs/ppc64le/libnvidia-encode.so b/thirdparty/nvcodec/Lib/linux/stubs/ppc64le/libnvidia-encode.so new file mode 100644 index 0000000..e6c00c0 Binary files /dev/null and b/thirdparty/nvcodec/Lib/linux/stubs/ppc64le/libnvidia-encode.so differ diff --git a/thirdparty/nvcodec/Lib/linux/stubs/x86_64/libnvcuvid.so b/thirdparty/nvcodec/Lib/linux/stubs/x86_64/libnvcuvid.so new file mode 100644 index 0000000..f08a209 Binary files /dev/null and b/thirdparty/nvcodec/Lib/linux/stubs/x86_64/libnvcuvid.so differ diff --git a/thirdparty/nvcodec/Lib/linux/stubs/x86_64/libnvidia-encode.so b/thirdparty/nvcodec/Lib/linux/stubs/x86_64/libnvidia-encode.so new file mode 100644 index 0000000..99934c7 Binary files /dev/null and b/thirdparty/nvcodec/Lib/linux/stubs/x86_64/libnvidia-encode.so differ diff --git a/thirdparty/nvcodec/Lib/x64/nvcuvid.lib b/thirdparty/nvcodec/Lib/x64/nvcuvid.lib new file mode 100644 index 0000000..3c18690 Binary files /dev/null and b/thirdparty/nvcodec/Lib/x64/nvcuvid.lib differ diff --git a/thirdparty/nvcodec/Lib/x64/nvencodeapi.lib b/thirdparty/nvcodec/Lib/x64/nvencodeapi.lib new file mode 100644 index 0000000..15d14de Binary files /dev/null and b/thirdparty/nvcodec/Lib/x64/nvencodeapi.lib differ diff --git a/thirdparty/nvcodec/Samples/CMakeLists.txt b/thirdparty/nvcodec/Samples/CMakeLists.txt new file mode 100644 index 0000000..9fb27ea --- /dev/null +++ b/thirdparty/nvcodec/Samples/CMakeLists.txt @@ -0,0 +1,111 @@ +# Copyright 2020 NVIDIA Corporation. All rights reserved. +# +# Please refer to the NVIDIA end user license agreement (EULA) associated +# with this source code for terms and conditions that govern your use of +# this software. Any use, reproduction, disclosure, or distribution of +# this software and related documentation outside the terms of the EULA +# is strictly prohibited. + +# 3.7 is required for FindVulkan module support in CMake. +cmake_minimum_required(VERSION 3.7) + +project(NvCodec) + +# Set C++11 for all projects and disable non-standard extensions +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_INSTALL_PREFIX .) + +set(NVCODEC_PUBLIC_INTERFACE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../Interface) +set(NVCODEC_UTILS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utils) +set(NV_CODEC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/NvCodec) +set(NV_ENC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/NvCodec/NvEncoder) +set(NV_DEC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/NvCodec/NvDecoder) +set(NV_APPENC_COMMON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/AppEncode/Common) +set(NV_APPDEC_COMMON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/AppDecode/Common) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(NVCODEC_SAMPLES_INSTALL_DIR ${CMAKE_BINARY_DIR}) +else() + set(NVCODEC_SAMPLES_INSTALL_DIR ${CMAKE_BINARY_DIR}) +endif() + +if(WIN32) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + Set(CUVID_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../Lib/x64/nvcuvid.lib) + set(NVENCODEAPI_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../Lib/x64/nvencodeapi.lib) + else() + Set(CUVID_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../Lib/Win32/nvcuvid.lib) + set(NVENCODEAPI_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../Lib/Win32/nvencodeapi.lib) + endif() +else () + find_library(CUVID_LIB nvcuvid) + find_library(NVENCODEAPI_LIB nvidia-encode) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + find_package(PkgConfig REQUIRED) + pkg_check_modules(PC_AVCODEC REQUIRED IMPORTED_TARGET libavcodec) + pkg_check_modules(PC_AVFORMAT REQUIRED IMPORTED_TARGET libavformat) + pkg_check_modules(PC_AVUTIL REQUIRED IMPORTED_TARGET libavutil) + pkg_check_modules(PC_SWRESAMPLE REQUIRED IMPORTED_TARGET libswresample) + + set(NV_FFMPEG_HDRS ${PC_AVCODEC_INCLUDE_DIRS}) + find_library(AVCODEC_LIBRARY NAMES avcodec + HINTS + ${PC_AVCODEC_LIBDIR} + ${PC_AVCODEC_LIBRARY_DIRS} + ) + find_library(AVFORMAT_LIBRARY NAMES avformat + HINTS + ${PC_AVFORMAT_LIBDIR} + ${PC_AVFORMAT_LIBRARY_DIRS} + ) + find_library(AVUTIL_LIBRARY NAMES avutil + HINTS + ${PC_AVUTIL_LIBDIR} + ${PC_AVUTIL_LIBRARY_DIRS} + ) + find_library(SWRESAMPLE_LIBRARY NAMES swresample + HINTS + ${PC_SWRESAMPLE_LIBDIR} + ${PC_SWRESAMPLE_LIBRARY_DIRS} + ) + set(AVCODEC_LIB ${AVCODEC_LIBRARY}) + set(AVFORMAT_LIB ${AVFORMAT_LIBRARY}) + set(AVUTIL_LIB ${AVUTIL_LIBRARY}) + set(SWRESAMPLE_LIB ${SWRESAMPLE_LIBRARY}) +endif() + + +if(WIN32) + add_subdirectory(AppEncode/AppEncD3D11) + add_subdirectory(AppEncode/AppEncD3D9) + add_subdirectory(AppDecode/AppDecD3D) +else () + #Need only linux Makefile for this + add_subdirectory(AppEncode/AppEncGL) +endif() + +add_subdirectory(AppEncode/AppEncCuda) +add_subdirectory(AppEncode/AppEncDec) +add_subdirectory(AppEncode/AppEncLowLatency) +add_subdirectory(AppEncode/AppEncME) +add_subdirectory(AppEncode/AppEncPerf) +add_subdirectory(AppEncode/AppEncQual) +add_subdirectory(AppEncode/AppMotionEstimationVkCuda) +add_subdirectory(AppTranscode/AppTrans) +add_subdirectory(AppTranscode/AppTransOneToN) +add_subdirectory(AppTranscode/AppTransPerf) +add_subdirectory(AppDecode/AppDec) +add_subdirectory(AppDecode/AppDecGL) +add_subdirectory(AppDecode/AppDecImageProvider) +add_subdirectory(AppDecode/AppDecLowLatency) +add_subdirectory(AppDecode/AppDecMem) +add_subdirectory(AppDecode/AppDecMultiFiles) +add_subdirectory(AppDecode/AppDecMultiInput) +add_subdirectory(AppDecode/AppDecPerf) + + diff --git a/thirdparty/nvcodec/Samples/Utils/BitDepth.cu b/thirdparty/nvcodec/Samples/Utils/BitDepth.cu new file mode 100644 index 0000000..7094872 --- /dev/null +++ b/thirdparty/nvcodec/Samples/Utils/BitDepth.cu @@ -0,0 +1,54 @@ +/* +* Copyright 2017-2020 NVIDIA Corporation. All rights reserved. +* +* Please refer to the NVIDIA end user license agreement (EULA) associated +* with this source code for terms and conditions that govern your use of +* this software. Any use, reproduction, disclosure, or distribution of +* this software and related documentation outside the terms of the EULA +* is strictly prohibited. +* +*/ + +#include +#include +#include + +static __global__ void ConvertUInt8ToUInt16Kernel(uint8_t *dpUInt8, uint16_t *dpUInt16, int nSrcPitch, int nDestPitch, int nWidth, int nHeight) +{ + int x = blockIdx.x * blockDim.x + threadIdx.x, + y = blockIdx.y * blockDim.y + threadIdx.y; + + if (x >= nWidth || y >= nHeight) + { + return; + } + int destStrideInPixels = nDestPitch / (sizeof(uint16_t)); + *(uchar2 *)&dpUInt16[y * destStrideInPixels + x] = uchar2{ 0, dpUInt8[y * nSrcPitch + x] }; +} + +static __global__ void ConvertUInt16ToUInt8Kernel(uint16_t *dpUInt16, uint8_t *dpUInt8, int nSrcPitch, int nDestPitch, int nWidth, int nHeight) +{ + int x = blockIdx.x * blockDim.x + threadIdx.x, + y = blockIdx.y * blockDim.y + threadIdx.y; + + if (x >= nWidth || y >= nHeight) + { + return; + } + int srcStrideInPixels = nSrcPitch / (sizeof(uint16_t)); + dpUInt8[y * nDestPitch + x] = ((uchar2 *)&dpUInt16[y * srcStrideInPixels + x])->y; +} + +void ConvertUInt8ToUInt16(uint8_t *dpUInt8, uint16_t *dpUInt16, int nSrcPitch, int nDestPitch, int nWidth, int nHeight) +{ + dim3 blockSize(16, 16, 1); + dim3 gridSize(((uint32_t)nWidth + blockSize.x - 1) / blockSize.x, ((uint32_t)nHeight + blockSize.y - 1) / blockSize.y, 1); + ConvertUInt8ToUInt16Kernel <<< gridSize, blockSize >>>(dpUInt8, dpUInt16, nSrcPitch, nDestPitch, nWidth, nHeight); +} + +void ConvertUInt16ToUInt8(uint16_t *dpUInt16, uint8_t *dpUInt8, int nSrcPitch, int nDestPitch, int nWidth, int nHeight) +{ + dim3 blockSize(16, 16, 1); + dim3 gridSize(((uint32_t)nWidth + blockSize.x - 1) / blockSize.x, ((uint32_t)nHeight + blockSize.y - 1) / blockSize.y, 1); + ConvertUInt16ToUInt8Kernel <<>>(dpUInt16, dpUInt8, nSrcPitch, nDestPitch, nWidth, nHeight); +} diff --git a/thirdparty/nvcodec/Samples/Utils/ColorSpace.cu b/thirdparty/nvcodec/Samples/Utils/ColorSpace.cu new file mode 100644 index 0000000..b6f86ed --- /dev/null +++ b/thirdparty/nvcodec/Samples/Utils/ColorSpace.cu @@ -0,0 +1,399 @@ +/* +* Copyright 2017-2020 NVIDIA Corporation. All rights reserved. +* +* Please refer to the NVIDIA end user license agreement (EULA) associated +* with this source code for terms and conditions that govern your use of +* this software. Any use, reproduction, disclosure, or distribution of +* this software and related documentation outside the terms of the EULA +* is strictly prohibited. +* +*/ + +#include "ColorSpace.h" + +__constant__ float matYuv2Rgb[3][3]; +__constant__ float matRgb2Yuv[3][3]; + + +void inline GetConstants(int iMatrix, float &wr, float &wb, int &black, int &white, int &max) { + black = 16; white = 235; + max = 255; + + switch (iMatrix) + { + case ColorSpaceStandard_BT709: + default: + wr = 0.2126f; wb = 0.0722f; + break; + + case ColorSpaceStandard_FCC: + wr = 0.30f; wb = 0.11f; + break; + + case ColorSpaceStandard_BT470: + case ColorSpaceStandard_BT601: + wr = 0.2990f; wb = 0.1140f; + break; + + case ColorSpaceStandard_SMPTE240M: + wr = 0.212f; wb = 0.087f; + break; + + case ColorSpaceStandard_BT2020: + case ColorSpaceStandard_BT2020C: + wr = 0.2627f; wb = 0.0593f; + // 10-bit only + black = 64 << 6; white = 940 << 6; + max = (1 << 16) - 1; + break; + } +} + +void SetMatYuv2Rgb(int iMatrix) { + float wr, wb; + int black, white, max; + GetConstants(iMatrix, wr, wb, black, white, max); + float mat[3][3] = { + 1.0f, 0.0f, (1.0f - wr) / 0.5f, + 1.0f, -wb * (1.0f - wb) / 0.5f / (1 - wb - wr), -wr * (1 - wr) / 0.5f / (1 - wb - wr), + 1.0f, (1.0f - wb) / 0.5f, 0.0f, + }; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + mat[i][j] = (float)(1.0 * max / (white - black) * mat[i][j]); + } + } + cudaMemcpyToSymbol(matYuv2Rgb, mat, sizeof(mat)); +} + +void SetMatRgb2Yuv(int iMatrix) { + float wr, wb; + int black, white, max; + GetConstants(iMatrix, wr, wb, black, white, max); + float mat[3][3] = { + wr, 1.0f - wb - wr, wb, + -0.5f * wr / (1.0f - wb), -0.5f * (1 - wb - wr) / (1.0f - wb), 0.5f, + 0.5f, -0.5f * (1.0f - wb - wr) / (1.0f - wr), -0.5f * wb / (1.0f - wr), + }; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + mat[i][j] = (float)(1.0 * (white - black) / max * mat[i][j]); + } + } + cudaMemcpyToSymbol(matRgb2Yuv, mat, sizeof(mat)); +} + +template +__device__ static T Clamp(T x, T lower, T upper) { + return x < lower ? lower : (x > upper ? upper : x); +} + +template +__device__ inline Rgb YuvToRgbForPixel(YuvUnit y, YuvUnit u, YuvUnit v) { + const int + low = 1 << (sizeof(YuvUnit) * 8 - 4), + mid = 1 << (sizeof(YuvUnit) * 8 - 1); + float fy = (int)y - low, fu = (int)u - mid, fv = (int)v - mid; + const float maxf = (1 << sizeof(YuvUnit) * 8) - 1.0f; + YuvUnit + r = (YuvUnit)Clamp(matYuv2Rgb[0][0] * fy + matYuv2Rgb[0][1] * fu + matYuv2Rgb[0][2] * fv, 0.0f, maxf), + g = (YuvUnit)Clamp(matYuv2Rgb[1][0] * fy + matYuv2Rgb[1][1] * fu + matYuv2Rgb[1][2] * fv, 0.0f, maxf), + b = (YuvUnit)Clamp(matYuv2Rgb[2][0] * fy + matYuv2Rgb[2][1] * fu + matYuv2Rgb[2][2] * fv, 0.0f, maxf); + + Rgb rgb{}; + const int nShift = abs((int)sizeof(YuvUnit) - (int)sizeof(rgb.c.r)) * 8; + if (sizeof(YuvUnit) >= sizeof(rgb.c.r)) { + rgb.c.r = r >> nShift; + rgb.c.g = g >> nShift; + rgb.c.b = b >> nShift; + } else { + rgb.c.r = r << nShift; + rgb.c.g = g << nShift; + rgb.c.b = b << nShift; + } + return rgb; +} + +template +__global__ static void YuvToRgbKernel(uint8_t *pYuv, int nYuvPitch, uint8_t *pRgb, int nRgbPitch, int nWidth, int nHeight) { + int x = (threadIdx.x + blockIdx.x * blockDim.x) * 2; + int y = (threadIdx.y + blockIdx.y * blockDim.y) * 2; + if (x + 1 >= nWidth || y + 1 >= nHeight) { + return; + } + + uint8_t *pSrc = pYuv + x * sizeof(YuvUnitx2) / 2 + y * nYuvPitch; + uint8_t *pDst = pRgb + x * sizeof(Rgb) + y * nRgbPitch; + + YuvUnitx2 l0 = *(YuvUnitx2 *)pSrc; + YuvUnitx2 l1 = *(YuvUnitx2 *)(pSrc + nYuvPitch); + YuvUnitx2 ch = *(YuvUnitx2 *)(pSrc + (nHeight - y / 2) * nYuvPitch); + + *(RgbIntx2 *)pDst = RgbIntx2 { + YuvToRgbForPixel(l0.x, ch.x, ch.y).d, + YuvToRgbForPixel(l0.y, ch.x, ch.y).d, + }; + *(RgbIntx2 *)(pDst + nRgbPitch) = RgbIntx2 { + YuvToRgbForPixel(l1.x, ch.x, ch.y).d, + YuvToRgbForPixel(l1.y, ch.x, ch.y).d, + }; +} + +template +__global__ static void Yuv444ToRgbKernel(uint8_t *pYuv, int nYuvPitch, uint8_t *pRgb, int nRgbPitch, int nWidth, int nHeight) { + int x = (threadIdx.x + blockIdx.x * blockDim.x) * 2; + int y = (threadIdx.y + blockIdx.y * blockDim.y); + if (x + 1 >= nWidth || y >= nHeight) { + return; + } + + uint8_t *pSrc = pYuv + x * sizeof(YuvUnitx2) / 2 + y * nYuvPitch; + uint8_t *pDst = pRgb + x * sizeof(Rgb) + y * nRgbPitch; + + YuvUnitx2 l0 = *(YuvUnitx2 *)pSrc; + YuvUnitx2 ch1 = *(YuvUnitx2 *)(pSrc + (nHeight * nYuvPitch)); + YuvUnitx2 ch2 = *(YuvUnitx2 *)(pSrc + (2 * nHeight * nYuvPitch)); + + *(RgbIntx2 *)pDst = RgbIntx2{ + YuvToRgbForPixel(l0.x, ch1.x, ch2.x).d, + YuvToRgbForPixel(l0.y, ch1.y, ch2.y).d, + }; +} + +template +__global__ static void YuvToRgbPlanarKernel(uint8_t *pYuv, int nYuvPitch, uint8_t *pRgbp, int nRgbpPitch, int nWidth, int nHeight) { + int x = (threadIdx.x + blockIdx.x * blockDim.x) * 2; + int y = (threadIdx.y + blockIdx.y * blockDim.y) * 2; + if (x + 1 >= nWidth || y + 1 >= nHeight) { + return; + } + + uint8_t *pSrc = pYuv + x * sizeof(YuvUnitx2) / 2 + y * nYuvPitch; + + YuvUnitx2 l0 = *(YuvUnitx2 *)pSrc; + YuvUnitx2 l1 = *(YuvUnitx2 *)(pSrc + nYuvPitch); + YuvUnitx2 ch = *(YuvUnitx2 *)(pSrc + (nHeight - y / 2) * nYuvPitch); + + Rgb rgb0 = YuvToRgbForPixel(l0.x, ch.x, ch.y), + rgb1 = YuvToRgbForPixel(l0.y, ch.x, ch.y), + rgb2 = YuvToRgbForPixel(l1.x, ch.x, ch.y), + rgb3 = YuvToRgbForPixel(l1.y, ch.x, ch.y); + + uint8_t *pDst = pRgbp + x * sizeof(RgbUnitx2) / 2 + y * nRgbpPitch; + *(RgbUnitx2 *)pDst = RgbUnitx2 {rgb0.v.x, rgb1.v.x}; + *(RgbUnitx2 *)(pDst + nRgbpPitch) = RgbUnitx2 {rgb2.v.x, rgb3.v.x}; + pDst += nRgbpPitch * nHeight; + *(RgbUnitx2 *)pDst = RgbUnitx2 {rgb0.v.y, rgb1.v.y}; + *(RgbUnitx2 *)(pDst + nRgbpPitch) = RgbUnitx2 {rgb2.v.y, rgb3.v.y}; + pDst += nRgbpPitch * nHeight; + *(RgbUnitx2 *)pDst = RgbUnitx2 {rgb0.v.z, rgb1.v.z}; + *(RgbUnitx2 *)(pDst + nRgbpPitch) = RgbUnitx2 {rgb2.v.z, rgb3.v.z}; +} + +template +__global__ static void Yuv444ToRgbPlanarKernel(uint8_t *pYuv, int nYuvPitch, uint8_t *pRgbp, int nRgbpPitch, int nWidth, int nHeight) { + int x = (threadIdx.x + blockIdx.x * blockDim.x) * 2; + int y = (threadIdx.y + blockIdx.y * blockDim.y); + if (x + 1 >= nWidth || y >= nHeight) { + return; + } + + uint8_t *pSrc = pYuv + x * sizeof(YuvUnitx2) / 2 + y * nYuvPitch; + + YuvUnitx2 l0 = *(YuvUnitx2 *)pSrc; + YuvUnitx2 ch1 = *(YuvUnitx2 *)(pSrc + (nHeight * nYuvPitch)); + YuvUnitx2 ch2 = *(YuvUnitx2 *)(pSrc + (2 * nHeight * nYuvPitch)); + + Rgb rgb0 = YuvToRgbForPixel(l0.x, ch1.x, ch2.x), + rgb1 = YuvToRgbForPixel(l0.y, ch1.y, ch2.y); + + + uint8_t *pDst = pRgbp + x * sizeof(RgbUnitx2) / 2 + y * nRgbpPitch; + *(RgbUnitx2 *)pDst = RgbUnitx2{ rgb0.v.x, rgb1.v.x }; + + pDst += nRgbpPitch * nHeight; + *(RgbUnitx2 *)pDst = RgbUnitx2{ rgb0.v.y, rgb1.v.y }; + + pDst += nRgbpPitch * nHeight; + *(RgbUnitx2 *)pDst = RgbUnitx2{ rgb0.v.z, rgb1.v.z }; +} + +template +void Nv12ToColor32(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + YuvToRgbKernel + <<>> + (dpNv12, nNv12Pitch, dpBgra, nBgraPitch, nWidth, nHeight); +} + +template +void Nv12ToColor64(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + YuvToRgbKernel + <<>> + (dpNv12, nNv12Pitch, dpBgra, nBgraPitch, nWidth, nHeight); +} + +template +void YUV444ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + Yuv444ToRgbKernel + <<>> + (dpYUV444, nPitch, dpBgra, nBgraPitch, nWidth, nHeight); +} + +template +void YUV444ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + Yuv444ToRgbKernel + <<>> + (dpYUV444, nPitch, dpBgra, nBgraPitch, nWidth, nHeight); +} + +template +void P016ToColor32(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + YuvToRgbKernel + <<>> + (dpP016, nP016Pitch, dpBgra, nBgraPitch, nWidth, nHeight); +} + +template +void P016ToColor64(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + YuvToRgbKernel + <<>> + (dpP016, nP016Pitch, dpBgra, nBgraPitch, nWidth, nHeight); +} + +template +void YUV444P16ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + Yuv444ToRgbKernel + <<>> + (dpYUV444, nPitch, dpBgra, nBgraPitch, nWidth, nHeight); +} + +template +void YUV444P16ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + Yuv444ToRgbKernel + <<>> + (dpYUV444, nPitch, dpBgra, nBgraPitch, nWidth, nHeight); +} + +template +void Nv12ToColorPlanar(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + YuvToRgbPlanarKernel + <<>> + (dpNv12, nNv12Pitch, dpBgrp, nBgrpPitch, nWidth, nHeight); +} + +template +void P016ToColorPlanar(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + YuvToRgbPlanarKernel + <<>> + (dpP016, nP016Pitch, dpBgrp, nBgrpPitch, nWidth, nHeight); +} + +template +void YUV444ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + Yuv444ToRgbPlanarKernel + <<>> + (dpYUV444, nPitch, dpBgrp, nBgrpPitch, nWidth, nHeight); +} + +template +void YUV444P16ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix) { + SetMatYuv2Rgb(iMatrix); + Yuv444ToRgbPlanarKernel + << > > + (dpYUV444, nPitch, dpBgrp, nBgrpPitch, nWidth, nHeight); +} + +// Explicit Instantiation +template void Nv12ToColor32(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void Nv12ToColor32(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void Nv12ToColor64(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void Nv12ToColor64(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void P016ToColor32(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void P016ToColor32(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void P016ToColor64(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void P016ToColor64(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444P16ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444P16ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444P16ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444P16ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix); +template void Nv12ToColorPlanar(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix); +template void Nv12ToColorPlanar(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix); +template void P016ToColorPlanar(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix); +template void P016ToColorPlanar(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444P16ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix); +template void YUV444P16ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix); + +template +__device__ inline YuvUnit RgbToY(RgbUnit r, RgbUnit g, RgbUnit b) { + const YuvUnit low = 1 << (sizeof(YuvUnit) * 8 - 4); + return matRgb2Yuv[0][0] * r + matRgb2Yuv[0][1] * g + matRgb2Yuv[0][2] * b + low; +} + +template +__device__ inline YuvUnit RgbToU(RgbUnit r, RgbUnit g, RgbUnit b) { + const YuvUnit mid = 1 << (sizeof(YuvUnit) * 8 - 1); + return matRgb2Yuv[1][0] * r + matRgb2Yuv[1][1] * g + matRgb2Yuv[1][2] * b + mid; +} + +template +__device__ inline YuvUnit RgbToV(RgbUnit r, RgbUnit g, RgbUnit b) { + const YuvUnit mid = 1 << (sizeof(YuvUnit) * 8 - 1); + return matRgb2Yuv[2][0] * r + matRgb2Yuv[2][1] * g + matRgb2Yuv[2][2] * b + mid; +} + +template +__global__ static void RgbToYuvKernel(uint8_t *pRgb, int nRgbPitch, uint8_t *pYuv, int nYuvPitch, int nWidth, int nHeight) { + int x = (threadIdx.x + blockIdx.x * blockDim.x) * 2; + int y = (threadIdx.y + blockIdx.y * blockDim.y) * 2; + if (x + 1 >= nWidth || y + 1 >= nHeight) { + return; + } + + uint8_t *pSrc = pRgb + x * sizeof(Rgb) + y * nRgbPitch; + RgbIntx2 int2a = *(RgbIntx2 *)pSrc; + RgbIntx2 int2b = *(RgbIntx2 *)(pSrc + nRgbPitch); + + Rgb rgb[4] = {int2a.x, int2a.y, int2b.x, int2b.y}; + decltype(Rgb::c.r) + r = (rgb[0].c.r + rgb[1].c.r + rgb[2].c.r + rgb[3].c.r) / 4, + g = (rgb[0].c.g + rgb[1].c.g + rgb[2].c.g + rgb[3].c.g) / 4, + b = (rgb[0].c.b + rgb[1].c.b + rgb[2].c.b + rgb[3].c.b) / 4; + + uint8_t *pDst = pYuv + x * sizeof(YuvUnitx2) / 2 + y * nYuvPitch; + *(YuvUnitx2 *)pDst = YuvUnitx2 { + RgbToY(rgb[0].c.r, rgb[0].c.g, rgb[0].c.b), + RgbToY(rgb[1].c.r, rgb[1].c.g, rgb[1].c.b), + }; + *(YuvUnitx2 *)(pDst + nYuvPitch) = YuvUnitx2 { + RgbToY(rgb[2].c.r, rgb[2].c.g, rgb[2].c.b), + RgbToY(rgb[3].c.r, rgb[3].c.g, rgb[3].c.b), + }; + *(YuvUnitx2 *)(pDst + (nHeight - y / 2) * nYuvPitch) = YuvUnitx2 { + RgbToU(r, g, b), + RgbToV(r, g, b), + }; +} + +void Bgra64ToP016(uint8_t *dpBgra, int nBgraPitch, uint8_t *dpP016, int nP016Pitch, int nWidth, int nHeight, int iMatrix) { + SetMatRgb2Yuv(iMatrix); + RgbToYuvKernel + <<>> + (dpBgra, nBgraPitch, dpP016, nP016Pitch, nWidth, nHeight); +} diff --git a/thirdparty/nvcodec/Samples/Utils/ColorSpace.h b/thirdparty/nvcodec/Samples/Utils/ColorSpace.h new file mode 100644 index 0000000..b5cb131 --- /dev/null +++ b/thirdparty/nvcodec/Samples/Utils/ColorSpace.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include + +typedef enum ColorSpaceStandard { + ColorSpaceStandard_BT709 = 1, + ColorSpaceStandard_Unspecified = 2, + ColorSpaceStandard_Reserved = 3, + ColorSpaceStandard_FCC = 4, + ColorSpaceStandard_BT470 = 5, + ColorSpaceStandard_BT601 = 6, + ColorSpaceStandard_SMPTE240M = 7, + ColorSpaceStandard_YCgCo = 8, + ColorSpaceStandard_BT2020 = 9, + ColorSpaceStandard_BT2020C = 10 +} ColorSpaceStandard; + +union BGRA32 { + uint32_t d; + uchar4 v; + struct { + uint8_t b, g, r, a; + } c; +}; + +union RGBA32 { + uint32_t d; + uchar4 v; + struct { + uint8_t r, g, b, a; + } c; +}; + +union BGRA64 { + uint64_t d; + ushort4 v; + struct { + uint16_t b, g, r, a; + } c; +}; + +union RGBA64 { + uint64_t d; + ushort4 v; + struct { + uint16_t r, g, b, a; + } c; +}; diff --git a/thirdparty/nvcodec/Samples/Utils/FFmpegDemuxer.h b/thirdparty/nvcodec/Samples/Utils/FFmpegDemuxer.h new file mode 100644 index 0000000..9431158 --- /dev/null +++ b/thirdparty/nvcodec/Samples/Utils/FFmpegDemuxer.h @@ -0,0 +1,357 @@ +/* +* Copyright 2017-2020 NVIDIA Corporation. All rights reserved. +* +* Please refer to the NVIDIA end user license agreement (EULA) associated +* with this source code for terms and conditions that govern your use of +* this software. Any use, reproduction, disclosure, or distribution of +* this software and related documentation outside the terms of the EULA +* is strictly prohibited. +* +*/ +#pragma once + +extern "C" { +#include +#include +#include +} +#include "NvCodecUtils.h" + +//--------------------------------------------------------------------------- +//! \file FFmpegDemuxer.h +//! \brief Provides functionality for stream demuxing +//! +//! This header file is used by Decode/Transcode apps to demux input video clips before decoding frames from it. +//--------------------------------------------------------------------------- + +/** +* @brief libavformat wrapper class. Retrieves the elementary encoded stream from the container format. +*/ +class FFmpegDemuxer { +private: + AVFormatContext *fmtc = NULL; + AVIOContext *avioc = NULL; + AVPacket pkt, pktFiltered; /*!< AVPacket stores compressed data typically exported by demuxers and then passed as input to decoders */ + AVBSFContext *bsfc = NULL; + + int iVideoStream; + bool bMp4H264, bMp4HEVC, bMp4MPEG4; + AVCodecID eVideoCodec; + AVPixelFormat eChromaFormat; + int nWidth, nHeight, nBitDepth, nBPP, nChromaHeight; + double timeBase = 0.0; + int64_t userTimeScale = 0; + + uint8_t *pDataWithHeader = NULL; + + unsigned int frameCount = 0; + +public: + class DataProvider { + public: + virtual ~DataProvider() {} + virtual int GetData(uint8_t *pBuf, int nBuf) = 0; + }; + +private: + + /** + * @brief Private constructor to initialize libavformat resources. + * @param fmtc - Pointer to AVFormatContext allocated inside avformat_open_input() + */ + FFmpegDemuxer(AVFormatContext *fmtc, int64_t timeScale = 1000 /*Hz*/) : fmtc(fmtc) { + if (!fmtc) { + LOG(ERROR) << "No AVFormatContext provided."; + return; + } + + LOG(INFO) << "Media format: " << fmtc->iformat->long_name << " (" << fmtc->iformat->name << ")"; + + ck(avformat_find_stream_info(fmtc, NULL)); + iVideoStream = av_find_best_stream(fmtc, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); + if (iVideoStream < 0) { + LOG(ERROR) << "FFmpeg error: " << __FILE__ << " " << __LINE__ << " " << "Could not find stream in input file"; + return; + } + + //fmtc->streams[iVideoStream]->need_parsing = AVSTREAM_PARSE_NONE; + eVideoCodec = fmtc->streams[iVideoStream]->codecpar->codec_id; + nWidth = fmtc->streams[iVideoStream]->codecpar->width; + nHeight = fmtc->streams[iVideoStream]->codecpar->height; + eChromaFormat = (AVPixelFormat)fmtc->streams[iVideoStream]->codecpar->format; + AVRational rTimeBase = fmtc->streams[iVideoStream]->time_base; + timeBase = av_q2d(rTimeBase); + userTimeScale = timeScale; + + // Set bit depth, chroma height, bits per pixel based on eChromaFormat of input + switch (eChromaFormat) + { + case AV_PIX_FMT_YUV420P10LE: + case AV_PIX_FMT_GRAY10LE: // monochrome is treated as 420 with chroma filled with 0x0 + nBitDepth = 10; + nChromaHeight = (nHeight + 1) >> 1; + nBPP = 2; + break; + case AV_PIX_FMT_YUV420P12LE: + nBitDepth = 12; + nChromaHeight = (nHeight + 1) >> 1; + nBPP = 2; + break; + case AV_PIX_FMT_YUV444P10LE: + nBitDepth = 10; + nChromaHeight = nHeight << 1; + nBPP = 2; + break; + case AV_PIX_FMT_YUV444P12LE: + nBitDepth = 12; + nChromaHeight = nHeight << 1; + nBPP = 2; + break; + case AV_PIX_FMT_YUV444P: + nBitDepth = 8; + nChromaHeight = nHeight << 1; + nBPP = 1; + break; + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUVJ420P: + case AV_PIX_FMT_YUVJ422P: // jpeg decoder output is subsampled to NV12 for 422/444 so treat it as 420 + case AV_PIX_FMT_YUVJ444P: // jpeg decoder output is subsampled to NV12 for 422/444 so treat it as 420 + case AV_PIX_FMT_GRAY8: // monochrome is treated as 420 with chroma filled with 0x0 + nBitDepth = 8; + nChromaHeight = (nHeight + 1) >> 1; + nBPP = 1; + break; + default: + LOG(WARNING) << "ChromaFormat not recognized. Assuming 420"; + eChromaFormat = AV_PIX_FMT_YUV420P; + nBitDepth = 8; + nChromaHeight = (nHeight + 1) >> 1; + nBPP = 1; + } + + bMp4H264 = eVideoCodec == AV_CODEC_ID_H264 && ( + !strcmp(fmtc->iformat->long_name, "QuickTime / MOV") + || !strcmp(fmtc->iformat->long_name, "FLV (Flash Video)") + || !strcmp(fmtc->iformat->long_name, "Matroska / WebM") + ); + bMp4HEVC = eVideoCodec == AV_CODEC_ID_HEVC && ( + !strcmp(fmtc->iformat->long_name, "QuickTime / MOV") + || !strcmp(fmtc->iformat->long_name, "FLV (Flash Video)") + || !strcmp(fmtc->iformat->long_name, "Matroska / WebM") + ); + + bMp4MPEG4 = eVideoCodec == AV_CODEC_ID_MPEG4 && ( + !strcmp(fmtc->iformat->long_name, "QuickTime / MOV") + || !strcmp(fmtc->iformat->long_name, "FLV (Flash Video)") + || !strcmp(fmtc->iformat->long_name, "Matroska / WebM") + ); + + //Initialize packet fields with default values + av_init_packet(&pkt); + pkt.data = NULL; + pkt.size = 0; + av_init_packet(&pktFiltered); + pktFiltered.data = NULL; + pktFiltered.size = 0; + + // Initialize bitstream filter and its required resources + if (bMp4H264) { + const AVBitStreamFilter *bsf = av_bsf_get_by_name("h264_mp4toannexb"); + if (!bsf) { + LOG(ERROR) << "FFmpeg error: " << __FILE__ << " " << __LINE__ << " " << "av_bsf_get_by_name() failed"; + return; + } + ck(av_bsf_alloc(bsf, &bsfc)); + avcodec_parameters_copy(bsfc->par_in, fmtc->streams[iVideoStream]->codecpar); + ck(av_bsf_init(bsfc)); + } + if (bMp4HEVC) { + const AVBitStreamFilter *bsf = av_bsf_get_by_name("hevc_mp4toannexb"); + if (!bsf) { + LOG(ERROR) << "FFmpeg error: " << __FILE__ << " " << __LINE__ << " " << "av_bsf_get_by_name() failed"; + return; + } + ck(av_bsf_alloc(bsf, &bsfc)); + avcodec_parameters_copy(bsfc->par_in, fmtc->streams[iVideoStream]->codecpar); + ck(av_bsf_init(bsfc)); + } + } + + AVFormatContext *CreateFormatContext(DataProvider *pDataProvider) { + + AVFormatContext *ctx = NULL; + if (!(ctx = avformat_alloc_context())) { + LOG(ERROR) << "FFmpeg error: " << __FILE__ << " " << __LINE__; + return NULL; + } + + uint8_t *avioc_buffer = NULL; + int avioc_buffer_size = 8 * 1024 * 1024; + avioc_buffer = (uint8_t *)av_malloc(avioc_buffer_size); + if (!avioc_buffer) { + LOG(ERROR) << "FFmpeg error: " << __FILE__ << " " << __LINE__; + return NULL; + } + avioc = avio_alloc_context(avioc_buffer, avioc_buffer_size, + 0, pDataProvider, &ReadPacket, NULL, NULL); + if (!avioc) { + LOG(ERROR) << "FFmpeg error: " << __FILE__ << " " << __LINE__; + return NULL; + } + ctx->pb = avioc; + + ck(avformat_open_input(&ctx, NULL, NULL, NULL)); + return ctx; + } + + /** + * @brief Allocate and return AVFormatContext*. + * @param szFilePath - Filepath pointing to input stream. + * @return Pointer to AVFormatContext + */ + AVFormatContext *CreateFormatContext(const char *szFilePath) { + avformat_network_init(); + + AVFormatContext *ctx = NULL; + ck(avformat_open_input(&ctx, szFilePath, NULL, NULL)); + return ctx; + } + +public: + FFmpegDemuxer(const char *szFilePath, int64_t timescale = 1000 /*Hz*/) : FFmpegDemuxer(CreateFormatContext(szFilePath), timescale) {} + FFmpegDemuxer(DataProvider *pDataProvider) : FFmpegDemuxer(CreateFormatContext(pDataProvider)) {avioc = fmtc->pb;} + ~FFmpegDemuxer() { + + if (!fmtc) { + return; + } + + if (pkt.data) { + av_packet_unref(&pkt); + } + if (pktFiltered.data) { + av_packet_unref(&pktFiltered); + } + + if (bsfc) { + av_bsf_free(&bsfc); + } + + avformat_close_input(&fmtc); + + if (avioc) { + av_freep(&avioc->buffer); + av_freep(&avioc); + } + + if (pDataWithHeader) { + av_free(pDataWithHeader); + } + } + AVCodecID GetVideoCodec() { + return eVideoCodec; + } + AVPixelFormat GetChromaFormat() { + return eChromaFormat; + } + int GetWidth() { + return nWidth; + } + int GetHeight() { + return nHeight; + } + int GetBitDepth() { + return nBitDepth; + } + int GetFrameSize() { + return nWidth * (nHeight + nChromaHeight) * nBPP; + } + bool Demux(uint8_t **ppVideo, int *pnVideoBytes, int64_t *pts = NULL) { + if (!fmtc) { + return false; + } + + *pnVideoBytes = 0; + + if (pkt.data) { + av_packet_unref(&pkt); + } + + int e = 0; + while ((e = av_read_frame(fmtc, &pkt)) >= 0 && pkt.stream_index != iVideoStream) { + av_packet_unref(&pkt); + } + if (e < 0) { + return false; + } + + if (bMp4H264 || bMp4HEVC) { + if (pktFiltered.data) { + av_packet_unref(&pktFiltered); + } + ck(av_bsf_send_packet(bsfc, &pkt)); + ck(av_bsf_receive_packet(bsfc, &pktFiltered)); + *ppVideo = pktFiltered.data; + *pnVideoBytes = pktFiltered.size; + if (pts) + *pts = (int64_t) (pktFiltered.pts * userTimeScale * timeBase); + } else { + + if (bMp4MPEG4 && (frameCount == 0)) { + + int extraDataSize = fmtc->streams[iVideoStream]->codecpar->extradata_size; + + if (extraDataSize > 0) { + + // extradata contains start codes 00 00 01. Subtract its size + pDataWithHeader = (uint8_t *)av_malloc(extraDataSize + pkt.size - 3*sizeof(uint8_t)); + + if (!pDataWithHeader) { + LOG(ERROR) << "FFmpeg error: " << __FILE__ << " " << __LINE__; + return false; + } + + memcpy(pDataWithHeader, fmtc->streams[iVideoStream]->codecpar->extradata, extraDataSize); + memcpy(pDataWithHeader+extraDataSize, pkt.data+3, pkt.size - 3*sizeof(uint8_t)); + + *ppVideo = pDataWithHeader; + *pnVideoBytes = extraDataSize + pkt.size - 3*sizeof(uint8_t); + } + + } else { + *ppVideo = pkt.data; + *pnVideoBytes = pkt.size; + } + + if (pts) + *pts = (int64_t)(pkt.pts * userTimeScale * timeBase); + } + + frameCount++; + + return true; + } + + static int ReadPacket(void *opaque, uint8_t *pBuf, int nBuf) { + return ((DataProvider *)opaque)->GetData(pBuf, nBuf); + } +}; + +inline cudaVideoCodec FFmpeg2NvCodecId(AVCodecID id) { + switch (id) { + case AV_CODEC_ID_MPEG1VIDEO : return cudaVideoCodec_MPEG1; + case AV_CODEC_ID_MPEG2VIDEO : return cudaVideoCodec_MPEG2; + case AV_CODEC_ID_MPEG4 : return cudaVideoCodec_MPEG4; + case AV_CODEC_ID_WMV3 : + case AV_CODEC_ID_VC1 : return cudaVideoCodec_VC1; + case AV_CODEC_ID_H264 : return cudaVideoCodec_H264; + case AV_CODEC_ID_HEVC : return cudaVideoCodec_HEVC; + case AV_CODEC_ID_VP8 : return cudaVideoCodec_VP8; + case AV_CODEC_ID_VP9 : return cudaVideoCodec_VP9; + case AV_CODEC_ID_MJPEG : return cudaVideoCodec_JPEG; + case AV_CODEC_ID_AV1 : return cudaVideoCodec_AV1; + default : return cudaVideoCodec_NumCodecs; + } +} + + diff --git a/thirdparty/nvcodec/Samples/Utils/FFmpegStreamer.h b/thirdparty/nvcodec/Samples/Utils/FFmpegStreamer.h new file mode 100644 index 0000000..2d4a6c6 --- /dev/null +++ b/thirdparty/nvcodec/Samples/Utils/FFmpegStreamer.h @@ -0,0 +1,109 @@ +/* +* Copyright 2017-2020 NVIDIA Corporation. All rights reserved. +* +* Please refer to the NVIDIA end user license agreement (EULA) associated +* with this source code for terms and conditions that govern your use of +* this software. Any use, reproduction, disclosure, or distribution of +* this software and related documentation outside the terms of the EULA +* is strictly prohibited. +* +*/ +#pragma once + +#include +#include +extern "C" { +#include +#include +#include +}; +#include "Logger.h" + +extern simplelogger::Logger *logger; + +class FFmpegStreamer { +private: + AVFormatContext *oc = NULL; + AVStream *vs = NULL; + int nFps = 0; + +public: + FFmpegStreamer(AVCodecID eCodecId, int nWidth, int nHeight, int nFps, const char *szInFilePath) : nFps(nFps) { + avformat_network_init(); + oc = avformat_alloc_context(); + if (!oc) { + LOG(ERROR) << "FFMPEG: avformat_alloc_context error"; + return; + } + + // Set format on oc + AVOutputFormat *fmt = av_guess_format("mpegts", NULL, NULL); + if (!fmt) { + LOG(ERROR) << "Invalid format"; + return; + } + fmt->video_codec = eCodecId; + + oc->oformat = fmt; + oc->url = av_strdup(szInFilePath); + LOG(INFO) << "Streaming destination: " << oc->url; + + // Add video stream to oc + vs = avformat_new_stream(oc, NULL); + if (!vs) { + LOG(ERROR) << "FFMPEG: Could not alloc video stream"; + return; + } + vs->id = 0; + + // Set video parameters + AVCodecParameters *vpar = vs->codecpar; + vpar->codec_id = fmt->video_codec; + vpar->codec_type = AVMEDIA_TYPE_VIDEO; + vpar->width = nWidth; + vpar->height = nHeight; + + // Everything is ready. Now open the output stream. + if (avio_open(&oc->pb, oc->url, AVIO_FLAG_WRITE) < 0) { + LOG(ERROR) << "FFMPEG: Could not open " << oc->url; + return ; + } + + // Write the container header + if (avformat_write_header(oc, NULL)) { + LOG(ERROR) << "FFMPEG: avformat_write_header error!"; + return; + } + } + ~FFmpegStreamer() { + if (oc) { + av_write_trailer(oc); + avio_close(oc->pb); + avformat_free_context(oc); + } + } + + bool Stream(uint8_t *pData, int nBytes, int nPts) { + AVPacket pkt = {0}; + av_init_packet(&pkt); + pkt.pts = av_rescale_q(nPts++, AVRational {1, nFps}, vs->time_base); + // No B-frames + pkt.dts = pkt.pts; + pkt.stream_index = vs->index; + pkt.data = pData; + pkt.size = nBytes; + + if(!memcmp(pData, "\x00\x00\x00\x01\x67", 5)) { + pkt.flags |= AV_PKT_FLAG_KEY; + } + + // Write the compressed frame into the output + int ret = av_write_frame(oc, &pkt); + av_write_frame(oc, NULL); + if (ret < 0) { + LOG(ERROR) << "FFMPEG: Error while writing video frame"; + } + + return true; + } +}; diff --git a/thirdparty/nvcodec/Samples/Utils/NvCodecUtils.h b/thirdparty/nvcodec/Samples/Utils/NvCodecUtils.h new file mode 100644 index 0000000..1cab90c --- /dev/null +++ b/thirdparty/nvcodec/Samples/Utils/NvCodecUtils.h @@ -0,0 +1,490 @@ +/* + * Copyright 2017-2020 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +//--------------------------------------------------------------------------- +//! \file NvCodecUtils.h +//! \brief Miscellaneous classes and error checking functions. +//! +//! Used by Transcode/Encode samples apps for reading input files, +//! mutithreading, performance measurement or colorspace conversion while +//! decoding. +//--------------------------------------------------------------------------- + +#pragma once +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cuda_cuda_h__ +inline bool check(CUresult e, int iLine, const char *szFile) { + if (e != CUDA_SUCCESS) { + const char *szErrName = NULL; + cuGetErrorName(e, &szErrName); + + return false; + } + return true; +} +#endif + +#ifdef __CUDA_RUNTIME_H__ +inline bool check(cudaError_t e, int iLine, const char *szFile) { + if (e != cudaSuccess) { + return false; + } + return true; +} +#endif + +#ifdef _NV_ENCODEAPI_H_ +inline bool check(NVENCSTATUS e, int iLine, const char *szFile) { + const char *aszErrName[] = { + "NV_ENC_SUCCESS", + "NV_ENC_ERR_NO_ENCODE_DEVICE", + "NV_ENC_ERR_UNSUPPORTED_DEVICE", + "NV_ENC_ERR_INVALID_ENCODERDEVICE", + "NV_ENC_ERR_INVALID_DEVICE", + "NV_ENC_ERR_DEVICE_NOT_EXIST", + "NV_ENC_ERR_INVALID_PTR", + "NV_ENC_ERR_INVALID_EVENT", + "NV_ENC_ERR_INVALID_PARAM", + "NV_ENC_ERR_INVALID_CALL", + "NV_ENC_ERR_OUT_OF_MEMORY", + "NV_ENC_ERR_ENCODER_NOT_INITIALIZED", + "NV_ENC_ERR_UNSUPPORTED_PARAM", + "NV_ENC_ERR_LOCK_BUSY", + "NV_ENC_ERR_NOT_ENOUGH_BUFFER", + "NV_ENC_ERR_INVALID_VERSION", + "NV_ENC_ERR_MAP_FAILED", + "NV_ENC_ERR_NEED_MORE_INPUT", + "NV_ENC_ERR_ENCODER_BUSY", + "NV_ENC_ERR_EVENT_NOT_REGISTERD", + "NV_ENC_ERR_GENERIC", + "NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY", + "NV_ENC_ERR_UNIMPLEMENTED", + "NV_ENC_ERR_RESOURCE_REGISTER_FAILED", + "NV_ENC_ERR_RESOURCE_NOT_REGISTERED", + "NV_ENC_ERR_RESOURCE_NOT_MAPPED", + }; + if (e != NV_ENC_SUCCESS) { + return false; + } + return true; +} +#endif + +#ifdef _WINERROR_ +inline bool check(HRESULT e, int iLine, const char *szFile) { + if (e != S_OK) { + std::stringstream stream; + stream << std::hex << std::uppercase << e; + + return false; + } + return true; +} +#endif + +#if defined(__gl_h_) || defined(__GL_H__) +inline bool check(GLenum e, int iLine, const char *szFile) { + if (e != 0) { + return false; + } + return true; +} +#endif + +inline bool check(int e, int iLine, const char *szFile) { + if (e < 0) { + return false; + } + return true; +} + +#define ck(call) check(call, __LINE__, __FILE__) + +/** + * @brief Wrapper class around std::thread + */ +class NvThread { + public: + NvThread() = default; + NvThread(const NvThread &) = delete; + NvThread &operator=(const NvThread &other) = delete; + + NvThread(std::thread &&thread) : t(std::move(thread)) {} + + NvThread(NvThread &&thread) : t(std::move(thread.t)) {} + + NvThread &operator=(NvThread &&other) { + t = std::move(other.t); + return *this; + } + + ~NvThread() { join(); } + + void join() { + if (t.joinable()) { + t.join(); + } + } + + private: + std::thread t; +}; + +#ifndef _WIN32 +#define _stricmp strcasecmp +#define _stat64 stat64 +#endif + +/** + * @brief Utility class to allocate buffer memory. Helps avoid I/O during the + * encode/decode loop in case of performance tests. + */ +class BufferedFileReader { + public: + /** + * @brief Constructor function to allocate appropriate memory and copy file + * contents into it + */ + BufferedFileReader(const char *szFileName, bool bPartial = false) { + struct _stat64 st; + + if (_stat64(szFileName, &st) != 0) { + return; + } + + nSize = st.st_size; + while (nSize) { + try { + pBuf = new uint8_t[(size_t)nSize]; + if (nSize != st.st_size) { + } + break; + } catch (std::bad_alloc) { + if (!bPartial) { + return; + } + nSize = (uint32_t)(nSize * 0.9); + } + } + + std::ifstream fpIn(szFileName, std::ifstream::in | std::ifstream::binary); + if (!fpIn) { + return; + } + + std::streamsize nRead = + fpIn.read(reinterpret_cast(pBuf), nSize).gcount(); + fpIn.close(); + + assert(nRead == nSize); + } + ~BufferedFileReader() { + if (pBuf) { + delete[] pBuf; + } + } + bool GetBuffer(uint8_t **ppBuf, uint64_t *pnSize) { + if (!pBuf) { + return false; + } + + *ppBuf = pBuf; + *pnSize = nSize; + return true; + } + + private: + uint8_t *pBuf = NULL; + uint64_t nSize = 0; +}; + +/** + * @brief Template class to facilitate color space conversion + */ +template +class YuvConverter { + public: + YuvConverter(int nWidth, int nHeight) : nWidth(nWidth), nHeight(nHeight) { + pQuad = new T[((nWidth + 1) / 2) * ((nHeight + 1) / 2)]; + } + ~YuvConverter() { delete[] pQuad; } + void PlanarToUVInterleaved(T *pFrame, int nPitch = 0) { + if (nPitch == 0) { + nPitch = nWidth; + } + + // sizes of source surface plane + int nSizePlaneY = nPitch * nHeight; + int nSizePlaneU = ((nPitch + 1) / 2) * ((nHeight + 1) / 2); + int nSizePlaneV = nSizePlaneU; + + T *puv = pFrame + nSizePlaneY; + if (nPitch == nWidth) { + memcpy(pQuad, puv, nSizePlaneU * sizeof(T)); + } else { + for (int i = 0; i < (nHeight + 1) / 2; i++) { + memcpy(pQuad + ((nWidth + 1) / 2) * i, puv + ((nPitch + 1) / 2) * i, + ((nWidth + 1) / 2) * sizeof(T)); + } + } + T *pv = puv + nSizePlaneU; + for (int y = 0; y < (nHeight + 1) / 2; y++) { + for (int x = 0; x < (nWidth + 1) / 2; x++) { + puv[y * nPitch + x * 2] = pQuad[y * ((nWidth + 1) / 2) + x]; + puv[y * nPitch + x * 2 + 1] = pv[y * ((nPitch + 1) / 2) + x]; + } + } + } + void UVInterleavedToPlanar(T *pFrame, int nPitch = 0) { + if (nPitch == 0) { + nPitch = nWidth; + } + + // sizes of source surface plane + int nSizePlaneY = nPitch * nHeight; + int nSizePlaneU = ((nPitch + 1) / 2) * ((nHeight + 1) / 2); + int nSizePlaneV = nSizePlaneU; + + T *puv = pFrame + nSizePlaneY, *pu = puv, *pv = puv + nSizePlaneU; + + // split chroma from interleave to planar + for (int y = 0; y < (nHeight + 1) / 2; y++) { + for (int x = 0; x < (nWidth + 1) / 2; x++) { + pu[y * ((nPitch + 1) / 2) + x] = puv[y * nPitch + x * 2]; + pQuad[y * ((nWidth + 1) / 2) + x] = puv[y * nPitch + x * 2 + 1]; + } + } + if (nPitch == nWidth) { + memcpy(pv, pQuad, nSizePlaneV * sizeof(T)); + } else { + for (int i = 0; i < (nHeight + 1) / 2; i++) { + memcpy(pv + ((nPitch + 1) / 2) * i, pQuad + ((nWidth + 1) / 2) * i, + ((nWidth + 1) / 2) * sizeof(T)); + } + } + } + + private: + T *pQuad; + int nWidth, nHeight; +}; + +/** + * @brief Utility class to measure elapsed time in seconds between the block of + * executed code + */ +class StopWatch { + public: + void Start() { t0 = std::chrono::high_resolution_clock::now(); } + double Stop() { + return std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch() - + t0.time_since_epoch()) + .count() / + 1.0e9; + } + + private: + std::chrono::high_resolution_clock::time_point t0; +}; + +template +class ConcurrentQueue { + public: + ConcurrentQueue() {} + ConcurrentQueue(size_t size) : maxSize(size) {} + ConcurrentQueue(const ConcurrentQueue &) = delete; + ConcurrentQueue &operator=(const ConcurrentQueue &) = delete; + + void setSize(size_t s) { maxSize = s; } + + void push_back(const T &value) { + // Do not use a std::lock_guard here. We will need to explicitly + // unlock before notify_one as the other waiting thread will + // automatically try to acquire mutex once it wakes up + // (which will happen on notify_one) + std::unique_lock lock(m_mutex); + auto wasEmpty = m_List.empty(); + + while (full()) { + m_cond.wait(lock); + } + + m_List.push_back(value); + if (wasEmpty && !m_List.empty()) { + lock.unlock(); + m_cond.notify_one(); + } + } + + T pop_front() { + std::unique_lock lock(m_mutex); + + while (m_List.empty()) { + m_cond.wait(lock); + } + auto wasFull = full(); + T data = std::move(m_List.front()); + m_List.pop_front(); + + if (wasFull && !full()) { + lock.unlock(); + m_cond.notify_one(); + } + + return data; + } + + T front() { + std::unique_lock lock(m_mutex); + + while (m_List.empty()) { + m_cond.wait(lock); + } + + return m_List.front(); + } + + size_t size() { + std::unique_lock lock(m_mutex); + return m_List.size(); + } + + bool empty() { + std::unique_lock lock(m_mutex); + return m_List.empty(); + } + void clear() { + std::unique_lock lock(m_mutex); + m_List.clear(); + } + + private: + bool full() { + if (m_List.size() == maxSize) return true; + return false; + } + + private: + std::list m_List; + std::mutex m_mutex; + std::condition_variable m_cond; + size_t maxSize; +}; + +inline void CheckInputFile(const char *szInFilePath) { + std::ifstream fpIn(szInFilePath, std::ios::in | std::ios::binary); + if (fpIn.fail()) { + std::ostringstream err; + err << "Unable to open input file: " << szInFilePath << std::endl; + throw std::invalid_argument(err.str()); + } +} + +inline void ValidateResolution(int nWidth, int nHeight) { + if (nWidth <= 0 || nHeight <= 0) { + std::ostringstream err; + err << "Please specify positive non zero resolution as -s WxH. Current " + "resolution is " + << nWidth << "x" << nHeight << std::endl; + throw std::invalid_argument(err.str()); + } +} + +template +void Nv12ToColor32(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, + int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0); +template +void Nv12ToColor64(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, + int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0); + +template +void P016ToColor32(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, + int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4); +template +void P016ToColor64(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, + int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4); + +template +void YUV444ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, + int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0); +template +void YUV444ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, + int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0); + +template +void YUV444P16ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, + int nBgraPitch, int nWidth, int nHeight, + int iMatrix = 4); +template +void YUV444P16ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, + int nBgraPitch, int nWidth, int nHeight, + int iMatrix = 4); + +template +void Nv12ToColorPlanar(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgrp, + int nBgrpPitch, int nWidth, int nHeight, + int iMatrix = 0); +template +void P016ToColorPlanar(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgrp, + int nBgrpPitch, int nWidth, int nHeight, + int iMatrix = 4); + +template +void YUV444ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, + int nBgrpPitch, int nWidth, int nHeight, + int iMatrix = 0); +template +void YUV444P16ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, + int nBgrpPitch, int nWidth, int nHeight, + int iMatrix = 4); + +void Bgra64ToP016(uint8_t *dpBgra, int nBgraPitch, uint8_t *dpP016, + int nP016Pitch, int nWidth, int nHeight, int iMatrix = 4); + +void ConvertUInt8ToUInt16(uint8_t *dpUInt8, uint16_t *dpUInt16, int nSrcPitch, + int nDestPitch, int nWidth, int nHeight); +void ConvertUInt16ToUInt8(uint16_t *dpUInt16, uint8_t *dpUInt8, int nSrcPitch, + int nDestPitch, int nWidth, int nHeight); + +void ResizeNv12(unsigned char *dpDstNv12, int nDstPitch, int nDstWidth, + int nDstHeight, unsigned char *dpSrcNv12, int nSrcPitch, + int nSrcWidth, int nSrcHeight, + unsigned char *dpDstNv12UV = nullptr); +void ResizeP016(unsigned char *dpDstP016, int nDstPitch, int nDstWidth, + int nDstHeight, unsigned char *dpSrcP016, int nSrcPitch, + int nSrcWidth, int nSrcHeight, + unsigned char *dpDstP016UV = nullptr); + +void ScaleYUV420(unsigned char *dpDstY, unsigned char *dpDstU, + unsigned char *dpDstV, int nDstPitch, int nDstChromaPitch, + int nDstWidth, int nDstHeight, unsigned char *dpSrcY, + unsigned char *dpSrcU, unsigned char *dpSrcV, int nSrcPitch, + int nSrcChromaPitch, int nSrcWidth, int nSrcHeight, + bool bSemiplanar); + +#ifdef __cuda_cuda_h__ +void ComputeCRC(uint8_t *pBuffer, uint32_t *crcValue, + CUstream_st *outputCUStream); +#endif diff --git a/thirdparty/nvcodec/Samples/Utils/NvEncoderCLIOptions.h b/thirdparty/nvcodec/Samples/Utils/NvEncoderCLIOptions.h new file mode 100644 index 0000000..252caf3 --- /dev/null +++ b/thirdparty/nvcodec/Samples/Utils/NvEncoderCLIOptions.h @@ -0,0 +1,644 @@ +/* +* Copyright 2017-2020 NVIDIA Corporation. All rights reserved. +* +* Please refer to the NVIDIA end user license agreement (EULA) associated +* with this source code for terms and conditions that govern your use of +* this software. Any use, reproduction, disclosure, or distribution of +* this software and related documentation outside the terms of the EULA +* is strictly prohibited. +* +*/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include "../Utils/Logger.h" + +extern simplelogger::Logger *logger; + +#ifndef _WIN32 +inline bool operator==(const GUID &guid1, const GUID &guid2) { + return !memcmp(&guid1, &guid2, sizeof(GUID)); +} + +inline bool operator!=(const GUID &guid1, const GUID &guid2) { + return !(guid1 == guid2); +} +#endif + +/* + * Helper class for parsing generic encoder options and preparing encoder + * initialization parameters. This class also provides some utility methods + * which generate verbose descriptions of the provided set of encoder + * initialization parameters. + */ +class NvEncoderInitParam { +public: + NvEncoderInitParam(const char *szParam = "", + std::function *pfuncInit = NULL, bool _bLowLatency = false) + : strParam(szParam), bLowLatency(_bLowLatency) + { + if (pfuncInit) { + funcInit = *pfuncInit; + } + + std::transform(strParam.begin(), strParam.end(), strParam.begin(), tolower); + std::istringstream ss(strParam); + tokens = std::vector { + std::istream_iterator(ss), + std::istream_iterator() + }; + + for (unsigned i = 0; i < tokens.size(); i++) + { + if (tokens[i] == "-codec" && ++i != tokens.size()) + { + ParseString("-codec", tokens[i], vCodec, szCodecNames, &guidCodec); + continue; + } + if (tokens[i] == "-preset" && ++i != tokens.size()) { + ParseString("-preset", tokens[i], vPreset, szPresetNames, &guidPreset); + continue; + } + if (tokens[i] == "-tuninginfo" && ++i != tokens.size()) + { + ParseString("-tuninginfo", tokens[i], vTuningInfo, szTuningInfoNames, &m_TuningInfo); + continue; + } + } + } + virtual ~NvEncoderInitParam() {} + virtual bool IsCodecH264() { + return GetEncodeGUID() == NV_ENC_CODEC_H264_GUID; + } + + virtual bool IsCodecHEVC() { + return GetEncodeGUID() == NV_ENC_CODEC_HEVC_GUID; + } + std::string GetHelpMessage(bool bMeOnly = false, bool bUnbuffered = false, bool bHide444 = false, bool bOutputInVidMem = false) + { + std::ostringstream oss; + + if (bOutputInVidMem && bMeOnly) + { + oss << "-codec Codec: " << "h264" << std::endl; + } + else + { + oss << "-codec Codec: " << szCodecNames << std::endl; + } + + oss << "-preset Preset: " << szPresetNames << std::endl + << "-profile H264: " << szH264ProfileNames; + + if (bOutputInVidMem && bMeOnly) + { + oss << std::endl; + } + else + { + oss << "; HEVC: " << szHevcProfileNames << std::endl; + } + + if (!bMeOnly) + { + if (bLowLatency == false) + oss << "-tuninginfo TuningInfo: " << szTuningInfoNames << std::endl; + else + oss << "-tuninginfo TuningInfo: " << szLowLatencyTuningInfoNames << std::endl; + oss << "-multipass Multipass: " << szMultipass << std::endl; + } + + if (!bHide444 && !bLowLatency) + { + oss << "-444 (Only for RGB input) YUV444 encode" << std::endl; + } + if (bMeOnly) return oss.str(); + oss << "-fps Frame rate" << std::endl; + + if (!bUnbuffered && !bLowLatency) + { + oss << "-bf Number of consecutive B-frames" << std::endl; + } + + if (!bLowLatency) + { + oss << "-rc Rate control mode: " << szRcModeNames << std::endl + << "-gop Length of GOP (Group of Pictures)" << std::endl + << "-bitrate Average bit rate, can be in unit of 1, K, M" << std::endl + << "-maxbitrate Max bit rate, can be in unit of 1, K, M" << std::endl + << "-vbvbufsize VBV buffer size in bits, can be in unit of 1, K, M" << std::endl + << "-vbvinit VBV initial delay in bits, can be in unit of 1, K, M" << std::endl + << "-aq Enable spatial AQ and set its stength (range 1-15, 0-auto)" << std::endl + << "-temporalaq (No value) Enable temporal AQ" << std::endl + << "-cq Target constant quality level for VBR mode (range 1-51, 0-auto)" << std::endl; + } + if (!bUnbuffered && !bLowLatency) + { + oss << "-lookahead Maximum depth of lookahead (range 0-(31 - number of B frames))" << std::endl; + } + oss << "-qmin Min QP value" << std::endl + << "-qmax Max QP value" << std::endl + << "-initqp Initial QP value" << std::endl; + if (!bLowLatency) + { + oss << "-constqp QP value for constqp rate control mode" << std::endl + << "Note: QP value can be in the form of qp_of_P_B_I or qp_P,qp_B,qp_I (no space)" << std::endl; + } + if (bUnbuffered && !bLowLatency) + { + oss << "Note: Options -bf and -lookahead are unavailable for this app" << std::endl; + } + return oss.str(); + } + + /** + * @brief Generate and return a string describing the values of the main/common + * encoder initialization parameters + */ + std::string MainParamToString(const NV_ENC_INITIALIZE_PARAMS *pParams) { + std::ostringstream os; + os + << "Encoding Parameters:" + << std::endl << "\tcodec : " << ConvertValueToString(vCodec, szCodecNames, pParams->encodeGUID) + << std::endl << "\tpreset : " << ConvertValueToString(vPreset, szPresetNames, pParams->presetGUID); + if (pParams->tuningInfo) + { + os << std::endl << "\ttuningInfo : " << ConvertValueToString(vTuningInfo, szTuningInfoNames, pParams->tuningInfo); + } + os + << std::endl << "\tprofile : " << ConvertValueToString(vProfile, szProfileNames, pParams->encodeConfig->profileGUID) + << std::endl << "\tchroma : " << ConvertValueToString(vChroma, szChromaNames, (pParams->encodeGUID == NV_ENC_CODEC_H264_GUID) ? pParams->encodeConfig->encodeCodecConfig.h264Config.chromaFormatIDC : pParams->encodeConfig->encodeCodecConfig.hevcConfig.chromaFormatIDC) + << std::endl << "\tbitdepth : " << ((pParams->encodeGUID == NV_ENC_CODEC_H264_GUID) ? 0 : pParams->encodeConfig->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8) + 8 + << std::endl << "\trc : " << ConvertValueToString(vRcMode, szRcModeNames, pParams->encodeConfig->rcParams.rateControlMode) + ; + if (pParams->encodeConfig->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CONSTQP) { + os << " (P,B,I=" << pParams->encodeConfig->rcParams.constQP.qpInterP << "," << pParams->encodeConfig->rcParams.constQP.qpInterB << "," << pParams->encodeConfig->rcParams.constQP.qpIntra << ")"; + } + os + << std::endl << "\tfps : " << pParams->frameRateNum << "/" << pParams->frameRateDen + << std::endl << "\tgop : " << (pParams->encodeConfig->gopLength == NVENC_INFINITE_GOPLENGTH ? "INF" : std::to_string(pParams->encodeConfig->gopLength)) + << std::endl << "\tbf : " << pParams->encodeConfig->frameIntervalP - 1 + << std::endl << "\tmultipass : " << pParams->encodeConfig->rcParams.multiPass + << std::endl << "\tsize : " << pParams->encodeWidth << "x" << pParams->encodeHeight + << std::endl << "\tbitrate : " << pParams->encodeConfig->rcParams.averageBitRate + << std::endl << "\tmaxbitrate : " << pParams->encodeConfig->rcParams.maxBitRate + << std::endl << "\tvbvbufsize : " << pParams->encodeConfig->rcParams.vbvBufferSize + << std::endl << "\tvbvinit : " << pParams->encodeConfig->rcParams.vbvInitialDelay + << std::endl << "\taq : " << (pParams->encodeConfig->rcParams.enableAQ ? (pParams->encodeConfig->rcParams.aqStrength ? std::to_string(pParams->encodeConfig->rcParams.aqStrength) : "auto") : "disabled") + << std::endl << "\ttemporalaq : " << (pParams->encodeConfig->rcParams.enableTemporalAQ ? "enabled" : "disabled") + << std::endl << "\tlookahead : " << (pParams->encodeConfig->rcParams.enableLookahead ? std::to_string(pParams->encodeConfig->rcParams.lookaheadDepth) : "disabled") + << std::endl << "\tcq : " << (unsigned int)pParams->encodeConfig->rcParams.targetQuality + << std::endl << "\tqmin : P,B,I=" << (int)pParams->encodeConfig->rcParams.minQP.qpInterP << "," << (int)pParams->encodeConfig->rcParams.minQP.qpInterB << "," << (int)pParams->encodeConfig->rcParams.minQP.qpIntra + << std::endl << "\tqmax : P,B,I=" << (int)pParams->encodeConfig->rcParams.maxQP.qpInterP << "," << (int)pParams->encodeConfig->rcParams.maxQP.qpInterB << "," << (int)pParams->encodeConfig->rcParams.maxQP.qpIntra + << std::endl << "\tinitqp : P,B,I=" << (int)pParams->encodeConfig->rcParams.initialRCQP.qpInterP << "," << (int)pParams->encodeConfig->rcParams.initialRCQP.qpInterB << "," << (int)pParams->encodeConfig->rcParams.initialRCQP.qpIntra + ; + return os.str(); + } + +public: + virtual GUID GetEncodeGUID() { return guidCodec; } + virtual GUID GetPresetGUID() { return guidPreset; } + virtual NV_ENC_TUNING_INFO GetTuningInfo() { return m_TuningInfo; } + + /* + * @brief Set encoder initialization parameters based on input options + * This method parses the tokens formed from the command line options + * provided to the application and sets the fields from NV_ENC_INITIALIZE_PARAMS + * based on the supplied values. + */ + virtual void SetInitParams(NV_ENC_INITIALIZE_PARAMS *pParams, NV_ENC_BUFFER_FORMAT eBufferFormat) + { + NV_ENC_CONFIG &config = *pParams->encodeConfig; + for (unsigned i = 0; i < tokens.size(); i++) + { + if ( + tokens[i] == "-codec" && ++i || + tokens[i] == "-preset" && ++i || + tokens[i] == "-tuninginfo" && ++i || + tokens[i] == "-multipass" && ++i != tokens.size() && ParseString("-multipass", tokens[i], vMultiPass, szMultipass, &config.rcParams.multiPass) || + tokens[i] == "-profile" && ++i != tokens.size() && (IsCodecH264() ? + ParseString("-profile", tokens[i], vH264Profile, szH264ProfileNames, &config.profileGUID) : + ParseString("-profile", tokens[i], vHevcProfile, szHevcProfileNames, &config.profileGUID)) || + tokens[i] == "-rc" && ++i != tokens.size() && ParseString("-rc", tokens[i], vRcMode, szRcModeNames, &config.rcParams.rateControlMode) || + tokens[i] == "-fps" && ++i != tokens.size() && ParseInt("-fps", tokens[i], &pParams->frameRateNum) || + tokens[i] == "-bf" && ++i != tokens.size() && ParseInt("-bf", tokens[i], &config.frameIntervalP) && ++config.frameIntervalP || + tokens[i] == "-bitrate" && ++i != tokens.size() && ParseBitRate("-bitrate", tokens[i], &config.rcParams.averageBitRate) || + tokens[i] == "-maxbitrate" && ++i != tokens.size() && ParseBitRate("-maxbitrate", tokens[i], &config.rcParams.maxBitRate) || + tokens[i] == "-vbvbufsize" && ++i != tokens.size() && ParseBitRate("-vbvbufsize", tokens[i], &config.rcParams.vbvBufferSize) || + tokens[i] == "-vbvinit" && ++i != tokens.size() && ParseBitRate("-vbvinit", tokens[i], &config.rcParams.vbvInitialDelay) || + tokens[i] == "-cq" && ++i != tokens.size() && ParseInt("-cq", tokens[i], &config.rcParams.targetQuality) || + tokens[i] == "-initqp" && ++i != tokens.size() && ParseQp("-initqp", tokens[i], &config.rcParams.initialRCQP) && (config.rcParams.enableInitialRCQP = true) || + tokens[i] == "-qmin" && ++i != tokens.size() && ParseQp("-qmin", tokens[i], &config.rcParams.minQP) && (config.rcParams.enableMinQP = true) || + tokens[i] == "-qmax" && ++i != tokens.size() && ParseQp("-qmax", tokens[i], &config.rcParams.maxQP) && (config.rcParams.enableMaxQP = true) || + tokens[i] == "-constqp" && ++i != tokens.size() && ParseQp("-constqp", tokens[i], &config.rcParams.constQP) || + tokens[i] == "-temporalaq" && (config.rcParams.enableTemporalAQ = true) + ) + { + continue; + } + if (tokens[i] == "-lookahead" && ++i != tokens.size() && ParseInt("-lookahead", tokens[i], &config.rcParams.lookaheadDepth)) + { + config.rcParams.enableLookahead = config.rcParams.lookaheadDepth > 0; + continue; + } + int aqStrength; + if (tokens[i] == "-aq" && ++i != tokens.size() && ParseInt("-aq", tokens[i], &aqStrength)) { + config.rcParams.enableAQ = true; + config.rcParams.aqStrength = aqStrength; + continue; + } + + if (tokens[i] == "-gop" && ++i != tokens.size() && ParseInt("-gop", tokens[i], &config.gopLength)) + { + if (IsCodecH264()) + { + config.encodeCodecConfig.h264Config.idrPeriod = config.gopLength; + } + else + { + config.encodeCodecConfig.hevcConfig.idrPeriod = config.gopLength; + } + continue; + } + + if (tokens[i] == "-444") + { + if (IsCodecH264()) + { + config.encodeCodecConfig.h264Config.chromaFormatIDC = 3; + } else + { + config.encodeCodecConfig.hevcConfig.chromaFormatIDC = 3; + } + continue; + } + + std::ostringstream errmessage; + errmessage << "Incorrect parameter: " << tokens[i] << std::endl; + errmessage << "Re-run the application with the -h option to get a list of the supported options."; + errmessage << std::endl; + + throw std::invalid_argument(errmessage.str()); + } + + if (IsCodecHEVC()) + { + if (eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) + { + config.encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 = 2; + } + } + + funcInit(pParams); + LOG(INFO) << NvEncoderInitParam().MainParamToString(pParams); + LOG(TRACE) << NvEncoderInitParam().FullParamToString(pParams); + } + +private: + /* + * Helper methods for parsing tokens (generated by splitting the command line) + * and performing conversions to the appropriate target type/value. + */ + template + bool ParseString(const std::string &strName, const std::string &strValue, const std::vector &vValue, const std::string &strValueNames, T *pValue) { + std::vector vstrValueName = split(strValueNames, ' '); + auto it = std::find(vstrValueName.begin(), vstrValueName.end(), strValue); + if (it == vstrValueName.end()) { + LOG(ERROR) << strName << " options: " << strValueNames; + return false; + } + *pValue = vValue[it - vstrValueName.begin()]; + return true; + } + template + std::string ConvertValueToString(const std::vector &vValue, const std::string &strValueNames, T value) { + auto it = std::find(vValue.begin(), vValue.end(), value); + if (it == vValue.end()) { + LOG(ERROR) << "Invalid value. Can't convert to one of " << strValueNames; + return std::string(); + } + return split(strValueNames, ' ')[it - vValue.begin()]; + } + bool ParseBitRate(const std::string &strName, const std::string &strValue, unsigned *pBitRate) { + try { + size_t l; + double r = std::stod(strValue, &l); + char c = strValue[l]; + if (c != 0 && c != 'k' && c != 'm') { + LOG(ERROR) << strName << " units: 1, K, M (lower case also allowed)"; + } + *pBitRate = (unsigned)((c == 'm' ? 1000000 : (c == 'k' ? 1000 : 1)) * r); + } catch (std::invalid_argument) { + return false; + } + return true; + } + template + bool ParseInt(const std::string &strName, const std::string &strValue, T *pInt) { + try { + *pInt = std::stoi(strValue); + } catch (std::invalid_argument) { + LOG(ERROR) << strName << " need a value of positive number"; + return false; + } + return true; + } + bool ParseQp(const std::string &strName, const std::string &strValue, NV_ENC_QP *pQp) { + std::vector vQp = split(strValue, ','); + try { + if (vQp.size() == 1) { + unsigned qp = (unsigned)std::stoi(vQp[0]); + *pQp = {qp, qp, qp}; + } else if (vQp.size() == 3) { + *pQp = {(unsigned)std::stoi(vQp[0]), (unsigned)std::stoi(vQp[1]), (unsigned)std::stoi(vQp[2])}; + } else { + LOG(ERROR) << strName << " qp_for_P_B_I or qp_P,qp_B,qp_I (no space is allowed)"; + return false; + } + } catch (std::invalid_argument) { + return false; + } + return true; + } + std::vector split(const std::string &s, char delim) { + std::stringstream ss(s); + std::string token; + std::vector tokens; + while (getline(ss, token, delim)) { + tokens.push_back(token); + } + return tokens; + } + +private: + std::string strParam; + std::function funcInit = [](NV_ENC_INITIALIZE_PARAMS *pParams){}; + std::vector tokens; + GUID guidCodec = NV_ENC_CODEC_H264_GUID; + GUID guidPreset = NV_ENC_PRESET_P3_GUID; + NV_ENC_TUNING_INFO m_TuningInfo = NV_ENC_TUNING_INFO_HIGH_QUALITY; + bool bLowLatency = false; + + const char *szCodecNames = "h264 hevc"; + std::vector vCodec = std::vector { + NV_ENC_CODEC_H264_GUID, + NV_ENC_CODEC_HEVC_GUID + }; + + const char *szChromaNames = "yuv420 yuv444"; + std::vector vChroma = std::vector + { + 1, 3 + }; + + const char *szPresetNames = "p1 p2 p3 p4 p5 p6 p7"; + std::vector vPreset = std::vector { + NV_ENC_PRESET_P1_GUID, + NV_ENC_PRESET_P2_GUID, + NV_ENC_PRESET_P3_GUID, + NV_ENC_PRESET_P4_GUID, + NV_ENC_PRESET_P5_GUID, + NV_ENC_PRESET_P6_GUID, + NV_ENC_PRESET_P7_GUID, + }; + + const char *szH264ProfileNames = "baseline main high high444"; + std::vector vH264Profile = std::vector { + NV_ENC_H264_PROFILE_BASELINE_GUID, + NV_ENC_H264_PROFILE_MAIN_GUID, + NV_ENC_H264_PROFILE_HIGH_GUID, + NV_ENC_H264_PROFILE_HIGH_444_GUID, + }; + const char *szHevcProfileNames = "main main10 frext"; + std::vector vHevcProfile = std::vector { + NV_ENC_HEVC_PROFILE_MAIN_GUID, + NV_ENC_HEVC_PROFILE_MAIN10_GUID, + NV_ENC_HEVC_PROFILE_FREXT_GUID, + }; + const char *szProfileNames = "(default) auto baseline(h264) main(h264) high(h264) high444(h264)" + " stereo(h264) progressiv_high(h264) constrained_high(h264)" + " main(hevc) main10(hevc) frext(hevc)"; + std::vector vProfile = std::vector { + GUID{}, + NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID, + NV_ENC_H264_PROFILE_BASELINE_GUID, + NV_ENC_H264_PROFILE_MAIN_GUID, + NV_ENC_H264_PROFILE_HIGH_GUID, + NV_ENC_H264_PROFILE_HIGH_444_GUID, + NV_ENC_H264_PROFILE_STEREO_GUID, + NV_ENC_H264_PROFILE_PROGRESSIVE_HIGH_GUID, + NV_ENC_H264_PROFILE_CONSTRAINED_HIGH_GUID, + NV_ENC_HEVC_PROFILE_MAIN_GUID, + NV_ENC_HEVC_PROFILE_MAIN10_GUID, + NV_ENC_HEVC_PROFILE_FREXT_GUID, + }; + + const char *szLowLatencyTuningInfoNames = "lowlatency ultralowlatency"; + const char *szTuningInfoNames = "hq lowlatency ultralowlatency lossless"; + std::vector vTuningInfo = std::vector{ + NV_ENC_TUNING_INFO_HIGH_QUALITY, + NV_ENC_TUNING_INFO_LOW_LATENCY, + NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY, + NV_ENC_TUNING_INFO_LOSSLESS + }; + + const char *szRcModeNames = "constqp vbr cbr"; + std::vector vRcMode = std::vector { + NV_ENC_PARAMS_RC_CONSTQP, + NV_ENC_PARAMS_RC_VBR, + NV_ENC_PARAMS_RC_CBR, + }; + + const char *szMultipass = "disabled qres fullres"; + std::vector vMultiPass = std::vector{ + NV_ENC_MULTI_PASS_DISABLED, + NV_ENC_TWO_PASS_QUARTER_RESOLUTION, + NV_ENC_TWO_PASS_FULL_RESOLUTION, + }; + + const char *szQpMapModeNames = "disabled emphasis_level_map delta_qp_map qp_map"; + std::vector vQpMapMode = std::vector { + NV_ENC_QP_MAP_DISABLED, + NV_ENC_QP_MAP_EMPHASIS, + NV_ENC_QP_MAP_DELTA, + NV_ENC_QP_MAP, + }; + + +public: + /* + * Generates and returns a string describing the values for each field in + * the NV_ENC_INITIALIZE_PARAMS structure (i.e. a description of the entire + * set of initialization parameters supplied to the API). + */ + std::string FullParamToString(const NV_ENC_INITIALIZE_PARAMS *pInitializeParams) { + std::ostringstream os; + os << "NV_ENC_INITIALIZE_PARAMS:" << std::endl + << "encodeGUID: " << ConvertValueToString(vCodec, szCodecNames, pInitializeParams->encodeGUID) << std::endl + << "presetGUID: " << ConvertValueToString(vPreset, szPresetNames, pInitializeParams->presetGUID) << std::endl; + if (pInitializeParams->tuningInfo) + { + os << "tuningInfo: " << ConvertValueToString(vTuningInfo, szTuningInfoNames, pInitializeParams->tuningInfo) << std::endl; + } + os + << "encodeWidth: " << pInitializeParams->encodeWidth << std::endl + << "encodeHeight: " << pInitializeParams->encodeHeight << std::endl + << "darWidth: " << pInitializeParams->darWidth << std::endl + << "darHeight: " << pInitializeParams->darHeight << std::endl + << "frameRateNum: " << pInitializeParams->frameRateNum << std::endl + << "frameRateDen: " << pInitializeParams->frameRateDen << std::endl + << "enableEncodeAsync: " << pInitializeParams->enableEncodeAsync << std::endl + << "reportSliceOffsets: " << pInitializeParams->reportSliceOffsets << std::endl + << "enableSubFrameWrite: " << pInitializeParams->enableSubFrameWrite << std::endl + << "enableExternalMEHints: " << pInitializeParams->enableExternalMEHints << std::endl + << "enableMEOnlyMode: " << pInitializeParams->enableMEOnlyMode << std::endl + << "enableWeightedPrediction: " << pInitializeParams->enableWeightedPrediction << std::endl + << "maxEncodeWidth: " << pInitializeParams->maxEncodeWidth << std::endl + << "maxEncodeHeight: " << pInitializeParams->maxEncodeHeight << std::endl + << "maxMEHintCountsPerBlock: " << pInitializeParams->maxMEHintCountsPerBlock << std::endl + ; + NV_ENC_CONFIG *pConfig = pInitializeParams->encodeConfig; + os << "NV_ENC_CONFIG:" << std::endl + << "profile: " << ConvertValueToString(vProfile, szProfileNames, pConfig->profileGUID) << std::endl + << "gopLength: " << pConfig->gopLength << std::endl + << "frameIntervalP: " << pConfig->frameIntervalP << std::endl + << "monoChromeEncoding: " << pConfig->monoChromeEncoding << std::endl + << "frameFieldMode: " << pConfig->frameFieldMode << std::endl + << "mvPrecision: " << pConfig->mvPrecision << std::endl + << "NV_ENC_RC_PARAMS:" << std::endl + << " rateControlMode: 0x" << std::hex << pConfig->rcParams.rateControlMode << std::dec << std::endl + << " constQP: " << pConfig->rcParams.constQP.qpInterP << ", " << pConfig->rcParams.constQP.qpInterB << ", " << pConfig->rcParams.constQP.qpIntra << std::endl + << " averageBitRate: " << pConfig->rcParams.averageBitRate << std::endl + << " maxBitRate: " << pConfig->rcParams.maxBitRate << std::endl + << " vbvBufferSize: " << pConfig->rcParams.vbvBufferSize << std::endl + << " vbvInitialDelay: " << pConfig->rcParams.vbvInitialDelay << std::endl + << " enableMinQP: " << pConfig->rcParams.enableMinQP << std::endl + << " enableMaxQP: " << pConfig->rcParams.enableMaxQP << std::endl + << " enableInitialRCQP: " << pConfig->rcParams.enableInitialRCQP << std::endl + << " enableAQ: " << pConfig->rcParams.enableAQ << std::endl + << " qpMapMode: " << ConvertValueToString(vQpMapMode, szQpMapModeNames, pConfig->rcParams.qpMapMode) << std::endl + << " multipass: " << ConvertValueToString(vMultiPass, szMultipass, pConfig->rcParams.multiPass) << std::endl + << " enableLookahead: " << pConfig->rcParams.enableLookahead << std::endl + << " disableIadapt: " << pConfig->rcParams.disableIadapt << std::endl + << " disableBadapt: " << pConfig->rcParams.disableBadapt << std::endl + << " enableTemporalAQ: " << pConfig->rcParams.enableTemporalAQ << std::endl + << " zeroReorderDelay: " << pConfig->rcParams.zeroReorderDelay << std::endl + << " enableNonRefP: " << pConfig->rcParams.enableNonRefP << std::endl + << " strictGOPTarget: " << pConfig->rcParams.strictGOPTarget << std::endl + << " aqStrength: " << pConfig->rcParams.aqStrength << std::endl + << " minQP: " << pConfig->rcParams.minQP.qpInterP << ", " << pConfig->rcParams.minQP.qpInterB << ", " << pConfig->rcParams.minQP.qpIntra << std::endl + << " maxQP: " << pConfig->rcParams.maxQP.qpInterP << ", " << pConfig->rcParams.maxQP.qpInterB << ", " << pConfig->rcParams.maxQP.qpIntra << std::endl + << " initialRCQP: " << pConfig->rcParams.initialRCQP.qpInterP << ", " << pConfig->rcParams.initialRCQP.qpInterB << ", " << pConfig->rcParams.initialRCQP.qpIntra << std::endl + << " temporallayerIdxMask: " << pConfig->rcParams.temporallayerIdxMask << std::endl + << " temporalLayerQP: " << (int)pConfig->rcParams.temporalLayerQP[0] << ", " << (int)pConfig->rcParams.temporalLayerQP[1] << ", " << (int)pConfig->rcParams.temporalLayerQP[2] << ", " << (int)pConfig->rcParams.temporalLayerQP[3] << ", " << (int)pConfig->rcParams.temporalLayerQP[4] << ", " << (int)pConfig->rcParams.temporalLayerQP[5] << ", " << (int)pConfig->rcParams.temporalLayerQP[6] << ", " << (int)pConfig->rcParams.temporalLayerQP[7] << std::endl + << " targetQuality: " << pConfig->rcParams.targetQuality << std::endl + << " lookaheadDepth: " << pConfig->rcParams.lookaheadDepth << std::endl; + if (pInitializeParams->encodeGUID == NV_ENC_CODEC_H264_GUID) { + os + << "NV_ENC_CODEC_CONFIG (H264):" << std::endl + << " enableStereoMVC: " << pConfig->encodeCodecConfig.h264Config.enableStereoMVC << std::endl + << " hierarchicalPFrames: " << pConfig->encodeCodecConfig.h264Config.hierarchicalPFrames << std::endl + << " hierarchicalBFrames: " << pConfig->encodeCodecConfig.h264Config.hierarchicalBFrames << std::endl + << " outputBufferingPeriodSEI: " << pConfig->encodeCodecConfig.h264Config.outputBufferingPeriodSEI << std::endl + << " outputPictureTimingSEI: " << pConfig->encodeCodecConfig.h264Config.outputPictureTimingSEI << std::endl + << " outputAUD: " << pConfig->encodeCodecConfig.h264Config.outputAUD << std::endl + << " disableSPSPPS: " << pConfig->encodeCodecConfig.h264Config.disableSPSPPS << std::endl + << " outputFramePackingSEI: " << pConfig->encodeCodecConfig.h264Config.outputFramePackingSEI << std::endl + << " outputRecoveryPointSEI: " << pConfig->encodeCodecConfig.h264Config.outputRecoveryPointSEI << std::endl + << " enableIntraRefresh: " << pConfig->encodeCodecConfig.h264Config.enableIntraRefresh << std::endl + << " enableConstrainedEncoding: " << pConfig->encodeCodecConfig.h264Config.enableConstrainedEncoding << std::endl + << " repeatSPSPPS: " << pConfig->encodeCodecConfig.h264Config.repeatSPSPPS << std::endl + << " enableVFR: " << pConfig->encodeCodecConfig.h264Config.enableVFR << std::endl + << " enableLTR: " << pConfig->encodeCodecConfig.h264Config.enableLTR << std::endl + << " qpPrimeYZeroTransformBypassFlag: " << pConfig->encodeCodecConfig.h264Config.qpPrimeYZeroTransformBypassFlag << std::endl + << " useConstrainedIntraPred: " << pConfig->encodeCodecConfig.h264Config.useConstrainedIntraPred << std::endl + << " level: " << pConfig->encodeCodecConfig.h264Config.level << std::endl + << " idrPeriod: " << pConfig->encodeCodecConfig.h264Config.idrPeriod << std::endl + << " separateColourPlaneFlag: " << pConfig->encodeCodecConfig.h264Config.separateColourPlaneFlag << std::endl + << " disableDeblockingFilterIDC: " << pConfig->encodeCodecConfig.h264Config.disableDeblockingFilterIDC << std::endl + << " numTemporalLayers: " << pConfig->encodeCodecConfig.h264Config.numTemporalLayers << std::endl + << " spsId: " << pConfig->encodeCodecConfig.h264Config.spsId << std::endl + << " ppsId: " << pConfig->encodeCodecConfig.h264Config.ppsId << std::endl + << " adaptiveTransformMode: " << pConfig->encodeCodecConfig.h264Config.adaptiveTransformMode << std::endl + << " fmoMode: " << pConfig->encodeCodecConfig.h264Config.fmoMode << std::endl + << " bdirectMode: " << pConfig->encodeCodecConfig.h264Config.bdirectMode << std::endl + << " entropyCodingMode: " << pConfig->encodeCodecConfig.h264Config.entropyCodingMode << std::endl + << " stereoMode: " << pConfig->encodeCodecConfig.h264Config.stereoMode << std::endl + << " intraRefreshPeriod: " << pConfig->encodeCodecConfig.h264Config.intraRefreshPeriod << std::endl + << " intraRefreshCnt: " << pConfig->encodeCodecConfig.h264Config.intraRefreshCnt << std::endl + << " maxNumRefFrames: " << pConfig->encodeCodecConfig.h264Config.maxNumRefFrames << std::endl + << " sliceMode: " << pConfig->encodeCodecConfig.h264Config.sliceMode << std::endl + << " sliceModeData: " << pConfig->encodeCodecConfig.h264Config.sliceModeData << std::endl + << " NV_ENC_CONFIG_H264_VUI_PARAMETERS:" << std::endl + << " overscanInfoPresentFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.overscanInfoPresentFlag << std::endl + << " overscanInfo: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.overscanInfo << std::endl + << " videoSignalTypePresentFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.videoSignalTypePresentFlag << std::endl + << " videoFormat: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.videoFormat << std::endl + << " videoFullRangeFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.videoFullRangeFlag << std::endl + << " colourDescriptionPresentFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.colourDescriptionPresentFlag << std::endl + << " colourPrimaries: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.colourPrimaries << std::endl + << " transferCharacteristics: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.transferCharacteristics << std::endl + << " colourMatrix: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.colourMatrix << std::endl + << " chromaSampleLocationFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.chromaSampleLocationFlag << std::endl + << " chromaSampleLocationTop: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.chromaSampleLocationTop << std::endl + << " chromaSampleLocationBot: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.chromaSampleLocationBot << std::endl + << " bitstreamRestrictionFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.bitstreamRestrictionFlag << std::endl + << " ltrNumFrames: " << pConfig->encodeCodecConfig.h264Config.ltrNumFrames << std::endl + << " ltrTrustMode: " << pConfig->encodeCodecConfig.h264Config.ltrTrustMode << std::endl + << " chromaFormatIDC: " << pConfig->encodeCodecConfig.h264Config.chromaFormatIDC << std::endl + << " maxTemporalLayers: " << pConfig->encodeCodecConfig.h264Config.maxTemporalLayers << std::endl; + } else if (pInitializeParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) { + os + << "NV_ENC_CODEC_CONFIG (HEVC):" << std::endl + << " level: " << pConfig->encodeCodecConfig.hevcConfig.level << std::endl + << " tier: " << pConfig->encodeCodecConfig.hevcConfig.tier << std::endl + << " minCUSize: " << pConfig->encodeCodecConfig.hevcConfig.minCUSize << std::endl + << " maxCUSize: " << pConfig->encodeCodecConfig.hevcConfig.maxCUSize << std::endl + << " useConstrainedIntraPred: " << pConfig->encodeCodecConfig.hevcConfig.useConstrainedIntraPred << std::endl + << " disableDeblockAcrossSliceBoundary: " << pConfig->encodeCodecConfig.hevcConfig.disableDeblockAcrossSliceBoundary << std::endl + << " outputBufferingPeriodSEI: " << pConfig->encodeCodecConfig.hevcConfig.outputBufferingPeriodSEI << std::endl + << " outputPictureTimingSEI: " << pConfig->encodeCodecConfig.hevcConfig.outputPictureTimingSEI << std::endl + << " outputAUD: " << pConfig->encodeCodecConfig.hevcConfig.outputAUD << std::endl + << " enableLTR: " << pConfig->encodeCodecConfig.hevcConfig.enableLTR << std::endl + << " disableSPSPPS: " << pConfig->encodeCodecConfig.hevcConfig.disableSPSPPS << std::endl + << " repeatSPSPPS: " << pConfig->encodeCodecConfig.hevcConfig.repeatSPSPPS << std::endl + << " enableIntraRefresh: " << pConfig->encodeCodecConfig.hevcConfig.enableIntraRefresh << std::endl + << " chromaFormatIDC: " << pConfig->encodeCodecConfig.hevcConfig.chromaFormatIDC << std::endl + << " pixelBitDepthMinus8: " << pConfig->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 << std::endl + << " idrPeriod: " << pConfig->encodeCodecConfig.hevcConfig.idrPeriod << std::endl + << " intraRefreshPeriod: " << pConfig->encodeCodecConfig.hevcConfig.intraRefreshPeriod << std::endl + << " intraRefreshCnt: " << pConfig->encodeCodecConfig.hevcConfig.intraRefreshCnt << std::endl + << " maxNumRefFramesInDPB: " << pConfig->encodeCodecConfig.hevcConfig.maxNumRefFramesInDPB << std::endl + << " ltrNumFrames: " << pConfig->encodeCodecConfig.hevcConfig.ltrNumFrames << std::endl + << " vpsId: " << pConfig->encodeCodecConfig.hevcConfig.vpsId << std::endl + << " spsId: " << pConfig->encodeCodecConfig.hevcConfig.spsId << std::endl + << " ppsId: " << pConfig->encodeCodecConfig.hevcConfig.ppsId << std::endl + << " sliceMode: " << pConfig->encodeCodecConfig.hevcConfig.sliceMode << std::endl + << " sliceModeData: " << pConfig->encodeCodecConfig.hevcConfig.sliceModeData << std::endl + << " maxTemporalLayersMinus1: " << pConfig->encodeCodecConfig.hevcConfig.maxTemporalLayersMinus1 << std::endl + << " NV_ENC_CONFIG_HEVC_VUI_PARAMETERS:" << std::endl + << " overscanInfoPresentFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.overscanInfoPresentFlag << std::endl + << " overscanInfo: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.overscanInfo << std::endl + << " videoSignalTypePresentFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.videoSignalTypePresentFlag << std::endl + << " videoFormat: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.videoFormat << std::endl + << " videoFullRangeFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.videoFullRangeFlag << std::endl + << " colourDescriptionPresentFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.colourDescriptionPresentFlag << std::endl + << " colourPrimaries: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.colourPrimaries << std::endl + << " transferCharacteristics: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.transferCharacteristics << std::endl + << " colourMatrix: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.colourMatrix << std::endl + << " chromaSampleLocationFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.chromaSampleLocationFlag << std::endl + << " chromaSampleLocationTop: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.chromaSampleLocationTop << std::endl + << " chromaSampleLocationBot: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.chromaSampleLocationBot << std::endl + << " bitstreamRestrictionFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.bitstreamRestrictionFlag << std::endl + << " ltrTrustMode: " << pConfig->encodeCodecConfig.hevcConfig.ltrTrustMode << std::endl; + } + return os.str(); + } +}; diff --git a/thirdparty/nvcodec/Samples/Utils/Resize.cu b/thirdparty/nvcodec/Samples/Utils/Resize.cu new file mode 100644 index 0000000..93c4b6b --- /dev/null +++ b/thirdparty/nvcodec/Samples/Utils/Resize.cu @@ -0,0 +1,192 @@ +/* +* Copyright 2017-2020 NVIDIA Corporation. All rights reserved. +* +* Please refer to the NVIDIA end user license agreement (EULA) associated +* with this source code for terms and conditions that govern your use of +* this software. Any use, reproduction, disclosure, or distribution of +* this software and related documentation outside the terms of the EULA +* is strictly prohibited. +* +*/ + +#include +#include "NvCodecUtils.h" + +template +static __global__ void Resize(cudaTextureObject_t texY, cudaTextureObject_t texUv, + uint8_t *pDst, uint8_t *pDstUV, int nPitch, int nWidth, int nHeight, + float fxScale, float fyScale) +{ + int ix = blockIdx.x * blockDim.x + threadIdx.x, + iy = blockIdx.y * blockDim.y + threadIdx.y; + + if (ix >= nWidth / 2 || iy >= nHeight / 2) { + return; + } + + int x = ix * 2, y = iy * 2; + typedef decltype(YuvUnitx2::x) YuvUnit; + const int MAX = (1 << (sizeof(YuvUnit) * 8)) - 1; + *(YuvUnitx2 *)(pDst + y * nPitch + x * sizeof(YuvUnit)) = YuvUnitx2 { + (YuvUnit)(tex2D(texY, x / fxScale, y / fyScale) * MAX), + (YuvUnit)(tex2D(texY, (x + 1) / fxScale, y / fyScale) * MAX) + }; + y++; + *(YuvUnitx2 *)(pDst + y * nPitch + x * sizeof(YuvUnit)) = YuvUnitx2 { + (YuvUnit)(tex2D(texY, x / fxScale, y / fyScale) * MAX), + (YuvUnit)(tex2D(texY, (x + 1) / fxScale, y / fyScale) * MAX) + }; + float2 uv = tex2D(texUv, ix / fxScale, (nHeight + iy) / fyScale + 0.5f); + *(YuvUnitx2 *)(pDstUV + iy * nPitch + ix * 2 * sizeof(YuvUnit)) = YuvUnitx2{ (YuvUnit)(uv.x * MAX), (YuvUnit)(uv.y * MAX) }; +} + +template +static void Resize(unsigned char *dpDst, unsigned char* dpDstUV, int nDstPitch, int nDstWidth, int nDstHeight, unsigned char *dpSrc, int nSrcPitch, int nSrcWidth, int nSrcHeight) { + cudaResourceDesc resDesc = {}; + resDesc.resType = cudaResourceTypePitch2D; + resDesc.res.pitch2D.devPtr = dpSrc; + resDesc.res.pitch2D.desc = cudaCreateChannelDesc(); + resDesc.res.pitch2D.width = nSrcWidth; + resDesc.res.pitch2D.height = nSrcHeight; + resDesc.res.pitch2D.pitchInBytes = nSrcPitch; + + cudaTextureDesc texDesc = {}; + texDesc.filterMode = cudaFilterModeLinear; + texDesc.readMode = cudaReadModeNormalizedFloat; + + cudaTextureObject_t texY=0; + ck(cudaCreateTextureObject(&texY, &resDesc, &texDesc, NULL)); + + resDesc.res.pitch2D.desc = cudaCreateChannelDesc(); + resDesc.res.pitch2D.width = nSrcWidth / 2; + resDesc.res.pitch2D.height = nSrcHeight * 3 / 2; + + cudaTextureObject_t texUv=0; + ck(cudaCreateTextureObject(&texUv, &resDesc, &texDesc, NULL)); + + Resize << > >(texY, texUv, dpDst, dpDstUV, + nDstPitch, nDstWidth, nDstHeight, 1.0f * nDstWidth / nSrcWidth, 1.0f * nDstHeight / nSrcHeight); + + ck(cudaDestroyTextureObject(texY)); + ck(cudaDestroyTextureObject(texUv)); +} + +void ResizeNv12(unsigned char *dpDstNv12, int nDstPitch, int nDstWidth, int nDstHeight, unsigned char *dpSrcNv12, int nSrcPitch, int nSrcWidth, int nSrcHeight, unsigned char* dpDstNv12UV) +{ + unsigned char* dpDstUV = dpDstNv12UV ? dpDstNv12UV : dpDstNv12 + (nDstPitch*nDstHeight); + return Resize(dpDstNv12, dpDstUV, nDstPitch, nDstWidth, nDstHeight, dpSrcNv12, nSrcPitch, nSrcWidth, nSrcHeight); +} + + +void ResizeP016(unsigned char *dpDstP016, int nDstPitch, int nDstWidth, int nDstHeight, unsigned char *dpSrcP016, int nSrcPitch, int nSrcWidth, int nSrcHeight, unsigned char* dpDstP016UV) +{ + unsigned char* dpDstUV = dpDstP016UV ? dpDstP016UV : dpDstP016 + (nDstPitch*nDstHeight); + return Resize(dpDstP016, dpDstUV, nDstPitch, nDstWidth, nDstHeight, dpSrcP016, nSrcPitch, nSrcWidth, nSrcHeight); +} + +static __global__ void Scale(cudaTextureObject_t texSrc, + uint8_t *pDst, int nPitch, int nWidth, int nHeight, + float fxScale, float fyScale) +{ + int x = blockIdx.x * blockDim.x + threadIdx.x, + y = blockIdx.y * blockDim.y + threadIdx.y; + + if (x >= nWidth || y >= nHeight) + { + return; + } + + *(unsigned char*)(pDst + (y * nPitch) + x) = (unsigned char)(fminf((tex2D(texSrc, x * fxScale, y * fyScale)) * 255.0f, 255.0f)); +} + +static __global__ void Scale_uv(cudaTextureObject_t texSrc, + uint8_t *pDst, int nPitch, int nWidth, int nHeight, + float fxScale, float fyScale) +{ + int x = blockIdx.x * blockDim.x + threadIdx.x, + y = blockIdx.y * blockDim.y + threadIdx.y; + + if (x >= nWidth || y >= nHeight) + { + return; + } + + float2 uv = tex2D(texSrc, x * fxScale, y * fyScale); + uchar2 uvOut = uchar2{ (unsigned char)(fminf(uv.x * 255.0f, 255.0f)), (unsigned char)(fminf(uv.y * 255.0f, 255.0f)) }; + + *(uchar2*)(pDst + (y * nPitch) + 2 * x) = uvOut; +} + +void ScaleKernelLaunch(unsigned char *dpDst, int nDstPitch, int nDstWidth, int nDstHeight, unsigned char *dpSrc, int nSrcPitch, int nSrcWidth, int nSrcHeight, bool bUVPlane = false) +{ + cudaResourceDesc resDesc = {}; + resDesc.resType = cudaResourceTypePitch2D; + resDesc.res.pitch2D.devPtr = dpSrc; + resDesc.res.pitch2D.desc = bUVPlane ? cudaCreateChannelDesc() : cudaCreateChannelDesc(); + resDesc.res.pitch2D.width = nSrcWidth; + resDesc.res.pitch2D.height = nSrcHeight; + resDesc.res.pitch2D.pitchInBytes = nSrcPitch; + + cudaTextureDesc texDesc = {}; + texDesc.filterMode = cudaFilterModeLinear; + texDesc.readMode = cudaReadModeNormalizedFloat; + + texDesc.addressMode[0] = cudaAddressModeClamp; + texDesc.addressMode[1] = cudaAddressModeClamp; + texDesc.addressMode[2] = cudaAddressModeClamp; + + cudaTextureObject_t texSrc = 0; + ck(cudaCreateTextureObject(&texSrc, &resDesc, &texDesc, NULL)); + + dim3 blockSize(16, 16, 1); + dim3 gridSize(((uint32_t)nDstWidth + blockSize.x - 1) / blockSize.x, ((uint32_t)nDstHeight + blockSize.y - 1) / blockSize.y, 1); + + if (bUVPlane) + { + Scale_uv << > >(texSrc, dpDst, + nDstPitch, nDstWidth, nDstHeight, 1.0f * nSrcWidth / nDstWidth, 1.0f * nSrcHeight / nDstHeight); + } + else + { + Scale << > >(texSrc, dpDst, + nDstPitch, nDstWidth, nDstHeight, 1.0f * nSrcWidth / nDstWidth, 1.0f * nSrcHeight / nDstHeight); + } + + ck(cudaGetLastError()); + ck(cudaDestroyTextureObject(texSrc)); +} + +void ScaleYUV420(unsigned char *dpDstY, + unsigned char* dpDstU, + unsigned char* dpDstV, + int nDstPitch, + int nDstChromaPitch, + int nDstWidth, + int nDstHeight, + unsigned char *dpSrcY, + unsigned char* dpSrcU, + unsigned char* dpSrcV, + int nSrcPitch, + int nSrcChromaPitch, + int nSrcWidth, + int nSrcHeight, + bool bSemiplanar) +{ + int chromaWidthDst = (nDstWidth + 1) / 2; + int chromaHeightDst = (nDstHeight + 1) / 2; + + int chromaWidthSrc = (nSrcWidth + 1) / 2; + int chromaHeightSrc = (nSrcHeight + 1) / 2; + + ScaleKernelLaunch(dpDstY, nDstPitch, nDstWidth, nDstHeight, dpSrcY, nSrcPitch, nSrcWidth, nSrcHeight); + + if (bSemiplanar) + { + ScaleKernelLaunch(dpDstU, nDstChromaPitch, chromaWidthDst, chromaHeightDst, dpSrcU, nSrcChromaPitch, chromaWidthSrc, chromaHeightSrc, true); + } + else + { + ScaleKernelLaunch(dpDstU, nDstChromaPitch, chromaWidthDst, chromaHeightDst, dpSrcU, nSrcChromaPitch, chromaWidthSrc, chromaHeightSrc); + ScaleKernelLaunch(dpDstV, nDstChromaPitch, chromaWidthDst, chromaHeightDst, dpSrcV, nSrcChromaPitch, chromaWidthSrc, chromaHeightSrc); + } +} diff --git a/thirdparty/nvcodec/Samples/Utils/crc.cu b/thirdparty/nvcodec/Samples/Utils/crc.cu new file mode 100644 index 0000000..4a08381 --- /dev/null +++ b/thirdparty/nvcodec/Samples/Utils/crc.cu @@ -0,0 +1,126 @@ +/* +* Copyright 2018-2020 NVIDIA Corporation. All rights reserved. +* +* Please refer to the NVIDIA end user license agreement (EULA) associated +* with this source code for terms and conditions that govern your use of +* this software. Any use, reproduction, disclosure, or distribution of +* this software and related documentation outside the terms of the EULA +* is strictly prohibited. +* +*/ + +#include +#include "NvCodecUtils.h" + +/* +* CRC32 lookup table +* Generated by the following routine +* int i, j; +* U032 crc; +* for (i = 0; i < 256; i++) +* { +* crc = i; +* for (j = 0; j < 8; j++) { // 8 reduction +* crc = (crc >> 1) ^ ((crc & 1) ? 0xEDB88320L : 0); +* } +* Crc32Table[i] = crc; +* } + */ +__device__ __constant__ uint32_t Crc32Table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +typedef struct _NV_ENC_ENCODE_OUT_PARAMS +{ + uint32_t version; /**< [out]: Struct version. */ + uint32_t bitstreamSizeInBytes; /**< [out]: Encoded bitstream size in bytes */ + uint32_t cycleCount; /**< [out]: Cycle count */ + uint32_t firstPassCycleCount; /**< [out]: First pass cycle count */ + uint32_t reserved[60]; /**< [out]: Reserved and must be set to 0 */ +} NV_ENC_ENCODE_OUT_PARAMS; + +static __global__ void ComputeCRCKernel(uint8_t *pBuffer, uint32_t *crcValue) +{ + NV_ENC_ENCODE_OUT_PARAMS *outParams = (NV_ENC_ENCODE_OUT_PARAMS *)pBuffer; + uint32_t bitstreamSize = outParams->bitstreamSizeInBytes; + uint8_t *pEncStream = pBuffer + sizeof(NV_ENC_ENCODE_OUT_PARAMS); + uint32_t crc=~0; + + for(uint32_t i = 0; i < bitstreamSize; i++) + { + crc = (crc >> 8) ^ Crc32Table[((uint8_t)(crc)) ^ (*pEncStream++)]; + } + + *crcValue = ~crc; +} + +void ComputeCRC(uint8_t *pBuffer, uint32_t *crcValue, cudaStream_t outputCUStream) +{ + dim3 blockSize(1, 1, 1); + dim3 gridSize(1, 1, 1); + + ComputeCRCKernel <<>>(pBuffer, crcValue); +} diff --git a/xmake.lua b/xmake.lua index 104d78c..710e64d 100644 --- a/xmake.lua +++ b/xmake.lua @@ -17,6 +17,7 @@ add_defines("ASIO_STANDALONE", "ASIO_HAS_STD_TYPE_TRAITS", "ASIO_HAS_STD_SHARED_ if is_os("windows") then add_defines("_WEBSOCKETPP_CPP11_INTERNAL_") add_links("ws2_32", "Bcrypt") + add_requires("cuda") elseif is_os("linux") then add_links("pthread") set_config("cxxflags", "-fPIC") @@ -57,10 +58,24 @@ target("ws") add_packages("asio") add_includedirs("thirdparty/websocketpp/include", {public = true}) +target("media") + set_kind("static") + add_deps("log") + add_packages("cuda") + add_links("cuda", "nvencodeapi", "nvcuvid") + add_files("src/media/video/encode/nvcodec/*.cpp", + "src/media/video/decode/nvcodec/*.cpp") + add_includedirs("src/media/video/encode/nvcodec", + "src/media/video/decode/nvcodec", + "thirdparty/nvcodec/Interface", + "thirdparty/nvcodec/Samples", {public = true}) + add_linkdirs("thirdparty/nvcodec/Lib/x64") + target("qos") set_kind("static") add_deps("log") - add_files("src/qos/*.cpp") + add_files("src/qos/kcp/*.c") + add_includedirs("src/qos/kcp", {public = true}) target("transmission") set_kind("static") @@ -72,9 +87,9 @@ target("transmission") target("pc") set_kind("static") add_deps("log") - add_deps("ws", "ice", "transmission", "inih", "common") + add_deps("ws", "ice", "transmission", "inih", "common", "media") add_files("src/pc/*.cpp") - add_packages("asio", "nlohmann_json") + add_packages("asio", "nlohmann_json", "cuda") add_includedirs("src/transmission", {public = true}) target("projectx") @@ -82,7 +97,7 @@ target("projectx") add_deps("log") add_deps("pc") add_files("src/rtc/*.cpp") - add_packages("asio", "nlohmann_json") + add_packages("asio", "nlohmann_json", "cuda") add_includedirs("src/rtc", "src/pc", "src/interface") add_rules("utils.symbols.export_all", {export_classes = true}) -- set_policy("build.merge_archive", true)