diff --git a/common/libs/VkCodecUtils/DecoderConfig.h b/common/libs/VkCodecUtils/DecoderConfig.h index 2b66fcc..1e30a5a 100644 --- a/common/libs/VkCodecUtils/DecoderConfig.h +++ b/common/libs/VkCodecUtils/DecoderConfig.h @@ -67,7 +67,6 @@ struct DecoderConfig { queueId = 0; gpuIndex = -1; forceParserType = VK_VIDEO_CODEC_OPERATION_NONE_KHR; - crcValues = nullptr; decoderQueueSize = 5; enablePostProcessFilter = -1, enableStreamDemuxing = false; @@ -76,11 +75,10 @@ struct DecoderConfig { enableHwLoadBalancing = false; selectVideoWithComputeQueue = false; enableVideoEncoder = false; - crcOutput = nullptr; outputy4m = false; outputcrcPerFrame = false; outputcrc = false; - crcOutputFile = nullptr; + crcOutputFileName.clear(); } using ProgramArgs = std::vector; @@ -292,7 +290,7 @@ struct DecoderConfig { }}, {"--crcoutfile", nullptr, 1, "Output file to store the CRC output into.", [this](const char **args, const ProgramArgs &a) { - crcOutputFile = fopen(args[0], "wt"); + crcOutputFileName = args[0]; return true; }}, {"--crcinit", nullptr, 1, "Initial value of the CRC separated by a comma, a set of CRCs can be specified with this commandline parameter", @@ -387,10 +385,6 @@ struct DecoderConfig { crcInitValue.push_back(0); } - - if (crcOutputFile == nullptr) { - crcOutputFile = stdout; - } } } @@ -434,7 +428,7 @@ struct DecoderConfig { return deviceUUID.empty() ? nullptr : deviceUUID.data(); } - FILE* crcOutputFile; + std::string crcOutputFileName; std::string appName; std::basic_string deviceUUID; int initialWidth; @@ -457,11 +451,9 @@ struct DecoderConfig { int queueId; VkVideoCodecOperationFlagBitsKHR forceParserType; std::vector crcInitValue; - uint32_t *crcValues; uint32_t deviceId; uint32_t decoderQueueSize; int32_t enablePostProcessFilter; - uint32_t *crcOutput; uint32_t enableStreamDemuxing : 1; uint32_t directMode : 1; uint32_t vsync : 1; diff --git a/common/libs/VkCodecUtils/VkVideoFrameToFile.cpp b/common/libs/VkCodecUtils/VkVideoFrameToFile.cpp new file mode 100644 index 0000000..d354aaf --- /dev/null +++ b/common/libs/VkCodecUtils/VkVideoFrameToFile.cpp @@ -0,0 +1,512 @@ +/* +* Copyright 2024 NVIDIA Corporation. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include +#include "nvidia_utils/vulkan/ycbcrvkinfo.h" +#include "VkVideoFrameToFile.h" +#include "VulkanDeviceContext.h" +#include "VulkanDeviceMemoryImpl.h" +#include "VkImageResource.h" +#include "VulkanDecodedFrame.h" +#include "Helpers.h" + +// CRC32 lookup table +static unsigned long 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 +}; + +static void getCRC(uint32_t *checksum, const uint8_t *inputBytes, size_t length, unsigned long crcTable[]) { + for (size_t i = 0; i < length; i += 1) { + *checksum = crcTable[inputBytes[i] ^ (*checksum & 0xff)] ^ (*checksum >> 8); + } +} + +template +static void CopyPlaneData(const uint8_t* pSrc, uint8_t* pDst, + size_t srcRowPitch, size_t dstRowPitch, + int32_t width, int32_t height, + size_t srcPixelStride = 1) { + const T* src = reinterpret_cast(pSrc); + T* dst = reinterpret_cast(pDst); + const size_t srcStride = srcRowPitch / sizeof(T); + const size_t dstStride = dstRowPitch / sizeof(T); + + for (int32_t y = 0; y < height; y++) { + if (srcPixelStride == 1) { + memcpy(dst, src, width * sizeof(T)); + } else { + for (int32_t x = 0; x < width; x++) { + dst[x] = src[x * srcPixelStride]; + } + } + src += srcStride; + dst += dstStride; + } +} + +class VkVideoFrameToFileImpl : public VkVideoFrameToFile { +public: + VkVideoFrameToFileImpl(bool outputy4m, + bool outputcrcPerFrame, + const char* crcOutputFile, + const std::vector& crcInitValue) + : m_refCount(0) + , m_outputFile(nullptr) + , m_pLinearMemory(nullptr) + , m_allocationSize(0) + , m_firstFrame(true) + , m_height(0) + , m_width(0) + , m_outputy4m(outputy4m) + , m_outputcrcPerFrame(outputcrcPerFrame) + , m_crcOutputFile(nullptr) + , m_crcInitValue(crcInitValue) + , m_crcAllocation() { + if (crcOutputFile != nullptr) { + m_crcOutputFile = fopen(crcOutputFile, "w"); + if (m_crcOutputFile && !m_crcInitValue.empty()) { + m_crcAllocation.resize(m_crcInitValue.size()); + for (size_t i = 0; i < m_crcInitValue.size(); i += 1) { + m_crcAllocation[i] = m_crcInitValue[i]; + } + } + } + } + + virtual ~VkVideoFrameToFileImpl() override { + if (m_pLinearMemory) { + delete[] m_pLinearMemory; + m_pLinearMemory = nullptr; + } + + if (m_outputFile) { + fclose(m_outputFile); + m_outputFile = nullptr; + } + + if (m_crcOutputFile) { + if (!m_crcAllocation.empty()) { + fprintf(m_crcOutputFile, "CRC: "); + for (size_t i = 0; i < m_crcInitValue.size(); i += 1) { + fprintf(m_crcOutputFile, "0x%08X ", m_crcAllocation[i]); + } + fprintf(m_crcOutputFile, "\n"); + } + + if (m_crcOutputFile != stdout) { + fclose(m_crcOutputFile); + } + m_crcOutputFile = nullptr; + } + } + + virtual int32_t AddRef() override { + return ++m_refCount; + } + + virtual int32_t Release() override { + uint32_t ret = --m_refCount; + if (ret == 0) { + delete this; + } + return ret; + } + + virtual size_t OutputFrame(VulkanDecodedFrame* pFrame, const VulkanDeviceContext* vkDevCtx) override { + if (!IsFileStreamValid()) { + return (size_t)-1; + } + + assert(pFrame != nullptr); + + VkSharedBaseObj imageResourceView; + pFrame->imageViews[VulkanDecodedFrame::IMAGE_VIEW_TYPE_LINEAR].GetImageResourceView(imageResourceView); + assert(!!imageResourceView); + assert(pFrame->pictureIndex != -1); + + VkSharedBaseObj imageResource = imageResourceView->GetImageResource(); + uint8_t* pOutputBuffer = EnsureAllocation(vkDevCtx, imageResource); + assert(pOutputBuffer != nullptr); + + assert((pFrame->displayWidth >= 0) && (pFrame->displayHeight >= 0)); + + WaitAndGetStatus(vkDevCtx, + *vkDevCtx, + pFrame->frameCompleteFence, + pFrame->queryPool, + pFrame->startQueryId, + pFrame->pictureIndex, false, "frameCompleteFence"); + + VkFormat format = imageResource->GetImageCreateInfo().format; + const VkMpFormatInfo* mpInfo = YcbcrVkFormatInfo(format); + size_t usedBufferSize = ConvertFrameToNv12(vkDevCtx, pFrame->displayWidth, pFrame->displayHeight, + imageResource, pOutputBuffer, mpInfo); + + if (m_outputcrcPerFrame && m_crcOutputFile) { + fprintf(m_crcOutputFile, "CRC Frame[%" PRId64 "]:", pFrame->displayOrder); + for (size_t i = 0; i < m_crcInitValue.size(); i += 1) { + uint32_t frameCrc = m_crcInitValue[i]; + getCRC(&frameCrc, pOutputBuffer, usedBufferSize, Crc32Table); + fprintf(m_crcOutputFile, "0x%08X ", frameCrc); + } + fprintf(m_crcOutputFile, "\n"); + if (m_crcOutputFile != stdout) { + fflush(m_crcOutputFile); + } + } + + if (!m_crcAllocation.empty()) { + for (size_t i = 0; i < m_crcAllocation.size(); i += 1) { + getCRC(&m_crcAllocation[i], pOutputBuffer, usedBufferSize, Crc32Table); + } + } + + if (m_outputy4m) { + return WriteFrameToFileY4M(0, usedBufferSize, pFrame->displayWidth, pFrame->displayHeight, mpInfo); + } else { + return WriteDataToFile(0, usedBufferSize); + } + } + + FILE* AttachFile(const char* fileName) { + if (m_outputFile) { + fclose(m_outputFile); + m_outputFile = nullptr; + } + + if (fileName != nullptr) { + m_outputFile = fopen(fileName, "wb"); + if (m_outputFile) { + return m_outputFile; + } + } + + return nullptr; + } + + bool IsFileStreamValid() const { + return m_outputFile != nullptr; + } + + operator bool() const { + return IsFileStreamValid(); + } + + size_t WriteDataToFile(size_t offset, size_t size) { + return fwrite(m_pLinearMemory + offset, size, 1, m_outputFile); + } + + size_t GetMaxFrameSize() { + return m_allocationSize; + } + + size_t WriteFrameToFileY4M(size_t offset, size_t size, size_t width, size_t height, + const VkMpFormatInfo* mpInfo) { + if (m_firstFrame != false) { + m_firstFrame = false; + fprintf(m_outputFile, "YUV4MPEG2 "); + fprintf(m_outputFile, "W%i H%i ", (int)width, (int)height); + m_height = height; + m_width = width; + fprintf(m_outputFile, "F24:1 "); + fprintf(m_outputFile, "Ip "); + fprintf(m_outputFile, "A1:1 "); + if (mpInfo->planesLayout.secondaryPlaneSubsampledX == false) { + fprintf(m_outputFile, "C444"); + } else { + fprintf(m_outputFile, "C420"); + } + + if (mpInfo->planesLayout.bpp != YCBCRA_8BPP) { + fprintf(m_outputFile, "p16"); + } + + fprintf(m_outputFile, "\n"); + } + + fprintf(m_outputFile, "FRAME"); + if ((m_width != width) || (m_height != height)) { + fprintf(m_outputFile, " "); + fprintf(m_outputFile, "W%i H%i", (int)width, (int)height); + m_height = height; + m_width = width; + } + + fprintf(m_outputFile, "\n"); + return WriteDataToFile(offset, size); + } + + size_t ConvertFrameToNv12(const VulkanDeviceContext* vkDevCtx, int32_t frameWidth, int32_t frameHeight, + VkSharedBaseObj& imageResource, + uint8_t* pOutBuffer, const VkMpFormatInfo* mpInfo) { + size_t outputBufferSize = 0; + VkDevice device = imageResource->GetDevice(); + VkImage srcImage = imageResource->GetImage(); + VkSharedBaseObj srcImageDeviceMemory(imageResource->GetMemory()); + + // Map the image and read the image data. + VkDeviceSize imageOffset = imageResource->GetImageDeviceMemoryOffset(); + VkDeviceSize maxSize = 0; + const uint8_t* readImagePtr = srcImageDeviceMemory->GetReadOnlyDataPtr(imageOffset, maxSize); + assert(readImagePtr != nullptr); + + int32_t secondaryPlaneHeight = frameHeight; + int32_t imageHeight = frameHeight; + bool isUnnormalizedRgba = false; + if (mpInfo && (mpInfo->planesLayout.layout == YCBCR_SINGLE_PLANE_UNNORMALIZED) && !(mpInfo->planesLayout.disjoint)) { + isUnnormalizedRgba = true; + } + + if (mpInfo && mpInfo->planesLayout.secondaryPlaneSubsampledY) { + secondaryPlaneHeight /= 2; + } + + VkImageSubresource subResource = {}; + VkSubresourceLayout layouts[3] = {}; + + if (mpInfo && !isUnnormalizedRgba) { + switch (mpInfo->planesLayout.layout) { + case YCBCR_SINGLE_PLANE_UNNORMALIZED: + case YCBCR_SINGLE_PLANE_INTERLEAVED: + subResource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT; + vkDevCtx->GetImageSubresourceLayout(device, srcImage, &subResource, &layouts[0]); + break; + case YCBCR_SEMI_PLANAR_CBCR_INTERLEAVED: + subResource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT; + vkDevCtx->GetImageSubresourceLayout(device, srcImage, &subResource, &layouts[0]); + subResource.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT; + vkDevCtx->GetImageSubresourceLayout(device, srcImage, &subResource, &layouts[1]); + break; + case YCBCR_PLANAR_CBCR_STRIDE_INTERLEAVED: + case YCBCR_PLANAR_CBCR_BLOCK_JOINED: + case YCBCR_PLANAR_STRIDE_PADDED: + subResource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT; + vkDevCtx->GetImageSubresourceLayout(device, srcImage, &subResource, &layouts[0]); + subResource.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT; + vkDevCtx->GetImageSubresourceLayout(device, srcImage, &subResource, &layouts[1]); + subResource.aspectMask = VK_IMAGE_ASPECT_PLANE_2_BIT; + vkDevCtx->GetImageSubresourceLayout(device, srcImage, &subResource, &layouts[2]); + break; + default: + assert(0); + } + } else { + vkDevCtx->GetImageSubresourceLayout(device, srcImage, &subResource, &layouts[0]); + } + + const bool is8Bit = (mpInfo->planesLayout.bpp == YCBCRA_8BPP); + const uint32_t bytesPerPixel = is8Bit ? 1 : 2; + const uint32_t numPlanes = 3; + + // Calculate plane layouts for output buffer + VkSubresourceLayout yuvPlaneLayouts[3] = {}; + yuvPlaneLayouts[0].offset = 0; + yuvPlaneLayouts[0].rowPitch = frameWidth * bytesPerPixel; + yuvPlaneLayouts[1].offset = yuvPlaneLayouts[0].rowPitch * frameHeight; + yuvPlaneLayouts[1].rowPitch = frameWidth * bytesPerPixel; + if (mpInfo && mpInfo->planesLayout.secondaryPlaneSubsampledX) { + yuvPlaneLayouts[1].rowPitch /= 2; + } + yuvPlaneLayouts[2].offset = yuvPlaneLayouts[1].offset + (yuvPlaneLayouts[1].rowPitch * secondaryPlaneHeight); + yuvPlaneLayouts[2].rowPitch = frameWidth * bytesPerPixel; + if (mpInfo && mpInfo->planesLayout.secondaryPlaneSubsampledX) { + yuvPlaneLayouts[2].rowPitch /= 2; + } + + // Copy the luma plane + const uint32_t numCompatiblePlanes = 1; + for (uint32_t plane = 0; plane < numCompatiblePlanes; plane++) { + const uint8_t* pSrc = readImagePtr + layouts[plane].offset; + uint8_t* pDst = pOutBuffer + yuvPlaneLayouts[plane].offset; + + if (is8Bit) { + CopyPlaneData(pSrc, pDst, layouts[plane].rowPitch, yuvPlaneLayouts[plane].rowPitch, + frameWidth, imageHeight); + } else { + CopyPlaneData(pSrc, pDst, layouts[plane].rowPitch, yuvPlaneLayouts[plane].rowPitch, + frameWidth, imageHeight); + } + } + + // Copy chroma planes + for (uint32_t plane = numCompatiblePlanes; plane < numPlanes; plane++) { + const uint32_t srcPlane = std::min(plane, mpInfo->planesLayout.numberOfExtraPlanes); + uint8_t* pDst = pOutBuffer + yuvPlaneLayouts[plane].offset; + const int32_t planeWidth = mpInfo->planesLayout.secondaryPlaneSubsampledX ? frameWidth / 2 : frameWidth; + + for (int32_t height = 0; height < secondaryPlaneHeight; height++) { + const uint8_t* pSrc; + if (srcPlane != plane) { + pSrc = readImagePtr + layouts[srcPlane].offset + ((plane - 1) * bytesPerPixel) + (layouts[srcPlane].rowPitch * height); + } else { + pSrc = readImagePtr + layouts[srcPlane].offset + (layouts[srcPlane].rowPitch * height); + } + + if (is8Bit) { + CopyPlaneData(pSrc, pDst, layouts[srcPlane].rowPitch, yuvPlaneLayouts[plane].rowPitch, + planeWidth, 1, 2); + } else { + CopyPlaneData(pSrc, pDst, layouts[srcPlane].rowPitch, yuvPlaneLayouts[plane].rowPitch, + planeWidth, 1, 2); + } + pDst += yuvPlaneLayouts[plane].rowPitch; + } + } + + // Calculate total buffer size + outputBufferSize = yuvPlaneLayouts[0].rowPitch * imageHeight; + if (mpInfo->planesLayout.numberOfExtraPlanes >= 1) { + outputBufferSize += yuvPlaneLayouts[1].rowPitch * secondaryPlaneHeight; + outputBufferSize += yuvPlaneLayouts[2].rowPitch * secondaryPlaneHeight; + } + + return outputBufferSize; + } + +private: + uint8_t* EnsureAllocation(const VulkanDeviceContext* vkDevCtx, + VkSharedBaseObj& imageResource) { + if (m_outputFile == nullptr) { + return nullptr; + } + + VkDeviceSize imageMemorySize = imageResource->GetImageDeviceMemorySize(); + + if ((m_pLinearMemory == nullptr) || (imageMemorySize > m_allocationSize)) { + if (m_outputFile) { + fflush(m_outputFile); + } + + if (m_pLinearMemory != nullptr) { + delete[] m_pLinearMemory; + m_pLinearMemory = nullptr; + } + + m_allocationSize = (size_t)(imageMemorySize); + m_pLinearMemory = new uint8_t[m_allocationSize]; + if (m_pLinearMemory == nullptr) { + return nullptr; + } + assert(m_pLinearMemory != nullptr); + } + return m_pLinearMemory; + } + +private: + std::atomic m_refCount; + FILE* m_outputFile; + uint8_t* m_pLinearMemory; + size_t m_allocationSize; + bool m_firstFrame; + size_t m_height; + size_t m_width; + bool m_outputy4m; + bool m_outputcrcPerFrame; + FILE* m_crcOutputFile; + std::vector m_crcInitValue; + std::vector m_crcAllocation; +}; + +// Define the static member for invalid instance +static VkSharedBaseObj s_invalidFrameToFile; +VkSharedBaseObj& VkVideoFrameToFile::invalidFrameToFile = s_invalidFrameToFile; + +VkResult VkVideoFrameToFile::Create(const char* fileName, + bool outputy4m, + bool outputcrcPerFrame, + const char* crcOutputFile, + const std::vector& crcInitValue, + VkSharedBaseObj& frameToFile) { + VkVideoFrameToFileImpl* newFrameToFile = new VkVideoFrameToFileImpl(outputy4m, outputcrcPerFrame, + crcOutputFile, crcInitValue); + if (!newFrameToFile) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + + FILE* outFile = newFrameToFile->AttachFile(fileName); + if ((fileName != nullptr) && (outFile == nullptr)) { + delete newFrameToFile; + return VK_ERROR_INITIALIZATION_FAILED; + } + + frameToFile = newFrameToFile; + return VK_SUCCESS; +} diff --git a/common/libs/VkCodecUtils/VkVideoFrameToFile.h b/common/libs/VkCodecUtils/VkVideoFrameToFile.h index 5a68504..ef0cac4 100644 --- a/common/libs/VkCodecUtils/VkVideoFrameToFile.h +++ b/common/libs/VkCodecUtils/VkVideoFrameToFile.h @@ -17,143 +17,63 @@ #ifndef _VKCODECUTILS_VKVIDEOFRAMETOFILE_H_ #define _VKCODECUTILS_VKVIDEOFRAMETOFILE_H_ -#include "nvidia_utils/vulkan/ycbcrvkinfo.h" - -class VkVideoFrameToFile { - +#include +#include +#include +#include +#include "VkCodecUtils/VkVideoRefCountBase.h" + +// Forward declarations +class VulkanDeviceContext; +class VulkanDecodedFrame; + +/** + * @brief Interface for writing video frames to a file. + * + * This class provides functionality to write decoded video frames to a file, + * with support for various formats including Y4M and CRC generation. + */ +class VkVideoFrameToFile : public VkVideoRefCountBase { public: - - VkVideoFrameToFile() - : m_outputFile(), - m_pLinearMemory() - , m_allocationSize() - , m_firstFrame(true) {} - - ~VkVideoFrameToFile() - { - if (m_pLinearMemory) { - delete[] m_pLinearMemory; - m_pLinearMemory = nullptr; - } - - if (m_outputFile) { - fclose(m_outputFile); - m_outputFile = nullptr; - } - } - - uint8_t* EnsureAllocation(const VulkanDeviceContext* vkDevCtx, - VkSharedBaseObj& imageResource) { - - if (m_outputFile == nullptr) { - return nullptr; - } - - VkDeviceSize imageMemorySize = imageResource->GetImageDeviceMemorySize(); - - if ((m_pLinearMemory == nullptr) || (imageMemorySize > m_allocationSize)) { - - if (m_outputFile) { - fflush(m_outputFile); - } - - if (m_pLinearMemory != nullptr) { - delete[] m_pLinearMemory; - m_pLinearMemory = nullptr; - } - - // Allocate the memory that will be dumped to file directly. - m_allocationSize = (size_t)(imageMemorySize); - m_pLinearMemory = new uint8_t[m_allocationSize]; - if (m_pLinearMemory == nullptr) { - return nullptr; - } - assert(m_pLinearMemory != nullptr); - } - return m_pLinearMemory; - } - - FILE* AttachFile(const char* fileName) { - - if (m_outputFile) { - fclose(m_outputFile); - m_outputFile = nullptr; - } - - if (fileName != nullptr) { - m_outputFile = fopen(fileName, "wb"); - if (m_outputFile) { - return m_outputFile; - } - } - - return nullptr; - } - - bool IsFileStreamValid() const - { - return m_outputFile != nullptr; - } - - operator bool() const { - return IsFileStreamValid(); - } - - size_t WriteDataToFile(size_t offset, size_t size) - { - return fwrite(m_pLinearMemory + offset, size, 1, m_outputFile); - } - - size_t GetMaxFrameSize() { - return m_allocationSize; - } - - size_t WriteFrameToFileY4M(size_t offset, size_t size, size_t width, size_t height, const VkMpFormatInfo *mpInfo) - { - // Output Frame. - if (m_firstFrame != false) { - m_firstFrame = false; - fprintf(m_outputFile, "YUV4MPEG2 "); - fprintf(m_outputFile, "W%i H%i ", (int)width, (int)height); - m_height = height; - m_width = width; - fprintf(m_outputFile, "F24:1 "); - fprintf(m_outputFile, "Ip "); - fprintf(m_outputFile, "A1:1 "); - if (mpInfo->planesLayout.secondaryPlaneSubsampledX == false) { - fprintf(m_outputFile, "C444"); - } else { - fprintf(m_outputFile, "C420"); - } - - if (mpInfo->planesLayout.bpp != YCBCRA_8BPP) { - fprintf(m_outputFile, "p16"); - } - - fprintf(m_outputFile, "\n"); - } - - fprintf(m_outputFile, "FRAME"); - if ((m_width != width) || (m_height != height)) { - fprintf(m_outputFile, " "); - fprintf(m_outputFile, "W%i H%i", (int)width, (int)height); - m_height = height; - m_width = width; - } - - fprintf(m_outputFile, "\n"); - return WriteDataToFile(offset, size); - } + /** @brief Reference to an invalid frame-to-file object used as default value */ + static VkSharedBaseObj& invalidFrameToFile; + + /** + * @brief Creates a new VkVideoFrameToFile instance + * + * @param fileName Output file name for the video frames + * @param outputy4m Whether to output in Y4M format + * @param outputcrcPerFrame Whether to generate CRC for each frame + * @param crcOutputFile File name for CRC output + * @param crcInitValue Initial CRC values + * @param frameToFile Reference to store the created instance + * @return VkResult VK_SUCCESS on success, error code otherwise + */ + static VkResult Create(const char* fileName, + bool outputy4m = true, + bool outputcrcPerFrame = false, + const char* crcOutputFile = nullptr, + const std::vector& crcInitValue = std::vector(), + VkSharedBaseObj& frameToFile = invalidFrameToFile); + + virtual ~VkVideoFrameToFile() = default; + + /** + * @brief Outputs a decoded frame to file + * + * @param pFrame Pointer to the decoded frame + * @param vkDevCtx Vulkan device context + * @return size_t Number of bytes written, (size_t)-1 on error + */ + virtual size_t OutputFrame(VulkanDecodedFrame* pFrame, const VulkanDeviceContext* vkDevCtx) = 0; + +protected: + VkVideoFrameToFile() = default; private: - FILE* m_outputFile; - uint8_t* m_pLinearMemory; - size_t m_allocationSize; - bool m_firstFrame; - size_t m_height; - size_t m_width; + // Prevent copying + VkVideoFrameToFile(const VkVideoFrameToFile&) = delete; + VkVideoFrameToFile& operator=(const VkVideoFrameToFile&) = delete; }; - - #endif /* _VKCODECUTILS_VKVIDEOFRAMETOFILE_H_ */ diff --git a/common/libs/VkCodecUtils/VulkanVideoProcessor.cpp b/common/libs/VkCodecUtils/VulkanVideoProcessor.cpp index 0ec166b..b229b50 100644 --- a/common/libs/VkCodecUtils/VulkanVideoProcessor.cpp +++ b/common/libs/VkCodecUtils/VulkanVideoProcessor.cpp @@ -36,12 +36,11 @@ int32_t VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, VkSharedBaseObj& videoStreamDemuxer, + VkSharedBaseObj& frameToFile, DecoderConfig& programConfig) { int32_t videoQueueIndx = programConfig.queueId; - const char* outputFileName = (programConfig.outputFileName.size() == 0) ? - nullptr : programConfig.outputFileName.c_str(); const uint32_t loopCount = programConfig.loopCount; const uint32_t startFrame = 0; const int32_t maxFrameCount = programConfig.maxFrameCount; @@ -84,14 +83,10 @@ int32_t VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, fprintf(stderr, "\nERROR: Create VulkanVideoFrameBuffer result: 0x%x\n", result); } - FILE* outFile = m_frameToFile.AttachFile(outputFileName); - if ((outputFileName != nullptr) && (outFile == nullptr)) { - fprintf( stderr, "Error opening the output file %s", outputFileName); - return -1; - } + m_frameToFile = frameToFile; uint32_t enableDecoderFeatures = 0; - if (outFile != nullptr) { + if (!!m_frameToFile) { enableDecoderFeatures |= VkVideoDecoder::ENABLE_LINEAR_OUTPUT; } @@ -502,61 +497,7 @@ size_t VulkanVideoProcessor::OutputFrameToFile(VulkanDecodedFrame* pFrame) return (size_t)-1; } - assert(pFrame != nullptr); - - VkSharedBaseObj imageResourceView; - pFrame->imageViews[VulkanDecodedFrame::IMAGE_VIEW_TYPE_LINEAR].GetImageResourceView(imageResourceView); - assert(!!imageResourceView); - assert(pFrame->pictureIndex != -1); - - VkSharedBaseObj imageResource = imageResourceView->GetImageResource(); - uint8_t* pOutputBuffer = m_frameToFile.EnsureAllocation(m_vkDevCtx, imageResource); - assert(pOutputBuffer != nullptr); - - // Needed allocation size can shrink, but may never grow. Frames will be allocated for maximum resolution upfront. - assert((pFrame->displayWidth >= 0) && (pFrame->displayHeight >= 0)); - - // Wait for decoder and copy engine to be done with this frame. - WaitAndGetStatus(m_vkDevCtx, - *m_vkDevCtx, - pFrame->frameCompleteFence, - pFrame->queryPool, - pFrame->startQueryId, - pFrame->pictureIndex, false, "frameCompleteFence"); - - // Convert frame to linear image format and write it to file. - VkFormat format = imageResource->GetImageCreateInfo().format; - const VkMpFormatInfo* mpInfo = YcbcrVkFormatInfo(format); - size_t usedBufferSize = ConvertFrameToNv12(m_vkDevCtx, pFrame->displayWidth, pFrame->displayHeight, imageResource, pOutputBuffer, mpInfo); - - // Output a crc for this frame. - if (m_settings.outputcrcPerFrame != 0) { - fprintf(m_settings.crcOutputFile, "CRC Frame[%" PRId64 "]:", pFrame->displayOrder); - size_t crcCount = m_settings.crcInitValue.size(); - for (size_t i = 0; i < crcCount; i += 1) { - uint32_t frameCrc = m_settings.crcInitValue[i]; - getCRC(&frameCrc, pOutputBuffer, usedBufferSize, Crc32Table); - fprintf(m_settings.crcOutputFile, "0x%08X ", frameCrc); - } - fprintf(m_settings.crcOutputFile, "\n"); - if (m_settings.crcOutputFile != stdout) { - fflush(m_settings.crcOutputFile); - } - } - - if ((m_settings.outputcrc != 0) && (m_settings.crcOutput != nullptr)) { - size_t crcCount = m_settings.crcInitValue.size(); - for (size_t i = 0; i < crcCount; i += 1) { - getCRC(&(m_settings.crcOutput[i]), pOutputBuffer, usedBufferSize, Crc32Table); - } - } - - // Write image to file. - if (m_settings.outputy4m != 0) { - return m_frameToFile.WriteFrameToFileY4M(0, usedBufferSize, pFrame->displayWidth, pFrame->displayHeight, mpInfo); - } else { - return m_frameToFile.WriteDataToFile(0, usedBufferSize); - } + return m_frameToFile->OutputFrame(pFrame, m_vkDevCtx); } uint32_t VulkanVideoProcessor::Restart(int64_t& bitstreamOffset) diff --git a/common/libs/VkCodecUtils/VulkanVideoProcessor.h b/common/libs/VkCodecUtils/VulkanVideoProcessor.h index 07ae799..ba4c444 100644 --- a/common/libs/VkCodecUtils/VulkanVideoProcessor.h +++ b/common/libs/VkCodecUtils/VulkanVideoProcessor.h @@ -43,6 +43,7 @@ class VulkanVideoProcessor : public VkVideoQueue { int32_t Initialize(const VulkanDeviceContext* vkDevCtx, VkSharedBaseObj& videoStreamDemuxer, + VkSharedBaseObj& frameToFile, DecoderConfig& programConfig); void Deinit(); @@ -78,12 +79,12 @@ class VulkanVideoProcessor : public VkVideoQueue { , m_vkVideoFrameBuffer() , m_vkVideoDecoder() , m_vkParser() + , m_frameToFile() , m_currentBitstreamOffset(0) , m_videoFrameNum(0) , m_videoStreamsCompleted(false) , m_usesStreamDemuxer(false) , m_usesFramePreparser(false) - , m_frameToFile() , m_loopCount(1) , m_startFrame(0) , m_maxFrameCount(-1) @@ -113,12 +114,12 @@ class VulkanVideoProcessor : public VkVideoQueue { VkSharedBaseObj m_vkVideoFrameBuffer; VkSharedBaseObj m_vkVideoDecoder; VkSharedBaseObj m_vkParser; + VkSharedBaseObj m_frameToFile; int64_t m_currentBitstreamOffset; uint32_t m_videoFrameNum; uint32_t m_videoStreamsCompleted : 1; uint32_t m_usesStreamDemuxer : 1; uint32_t m_usesFramePreparser : 1; - VkVideoFrameToFile m_frameToFile; int32_t m_loopCount; uint32_t m_startFrame; int32_t m_maxFrameCount; diff --git a/vk_video_decoder/demos/vk-video-dec/CMakeLists.txt b/vk_video_decoder/demos/vk-video-dec/CMakeLists.txt index 1807cae..628cde1 100644 --- a/vk_video_decoder/demos/vk-video-dec/CMakeLists.txt +++ b/vk_video_decoder/demos/vk-video-dec/CMakeLists.txt @@ -47,9 +47,9 @@ set(sources ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/nvVkFormats.cpp ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanBistreamBufferImpl.h ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanBistreamBufferImpl.cpp - ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/crcgenerator.cpp ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanCommandBufferPool.cpp ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanCommandBufferPool.h + ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VkVideoFrameToFile.cpp ${VK_VIDEO_DECODER_LIBS_SOURCE_ROOT}/VkDecoderUtils/FFmpegDemuxer.cpp ${VK_VIDEO_DECODER_LIBS_SOURCE_ROOT}/VkDecoderUtils/VideoStreamDemuxer.cpp ${VK_VIDEO_DECODER_LIBS_SOURCE_ROOT}/VkDecoderUtils/VideoStreamDemuxer.h diff --git a/vk_video_decoder/demos/vk-video-dec/Main.cpp b/vk_video_decoder/demos/vk-video-dec/Main.cpp index 6e6fb27..efd6786 100644 --- a/vk_video_decoder/demos/vk-video-dec/Main.cpp +++ b/vk_video_decoder/demos/vk-video-dec/Main.cpp @@ -26,23 +26,13 @@ #include "VkCodecUtils/VulkanVideoProcessor.h" #include "VkCodecUtils/VulkanDecoderFrameProcessor.h" #include "VkShell/Shell.h" +#include "VkCodecUtils/VkVideoFrameToFile.h" int main(int argc, const char **argv) { DecoderConfig decoderConfig(argv[0]); decoderConfig.ParseArgs(argc, argv); - // In the regular application usecase the CRC output variables are allocated here and also output as part of main. - // In the library case it is up to the caller of the library to allocate the values and initialize them. - std::vector crcAllocation; - crcAllocation.resize(decoderConfig.crcInitValue.size()); - if (crcAllocation.empty() == false) { - decoderConfig.crcOutput = &crcAllocation[0]; - for (size_t i = 0; i < decoderConfig.crcInitValue.size(); i += 1) { - crcAllocation[i] = decoderConfig.crcInitValue[i]; - } - } - VulkanDeviceContext vkDevCtxt; VkResult result = vkDevCtxt.InitVulkanDecoderDevice(decoderConfig.appName.c_str(), VK_NULL_HANDLE, @@ -156,7 +146,23 @@ int main(int argc, const char **argv) { if (result != VK_SUCCESS) { return -1; } - vulkanVideoProcessor->Initialize(&vkDevCtxt, videoStreamDemuxer, decoderConfig); + + VkSharedBaseObj frameToFile; + if (!decoderConfig.outputFileName.empty()) { + const char* crcOutputFile = decoderConfig.outputcrcPerFrame ? decoderConfig.crcOutputFileName.c_str() : nullptr; + result = VkVideoFrameToFile::Create(decoderConfig.outputFileName.c_str(), + decoderConfig.outputy4m, + decoderConfig.outputcrcPerFrame, + crcOutputFile, + decoderConfig.crcInitValue, + frameToFile); + if (result != VK_SUCCESS) { + fprintf(stderr, "Error creating output file %s\n", decoderConfig.outputFileName.c_str()); + return -1; + } + } + + vulkanVideoProcessor->Initialize(&vkDevCtxt, videoStreamDemuxer, frameToFile, decoderConfig); VkSharedBaseObj> videoQueue(vulkanVideoProcessor); DecoderFrameProcessorState frameProcessor(&vkDevCtxt, videoQueue, 0); @@ -219,7 +225,23 @@ int main(int argc, const char **argv) { std::cerr << "Error creating the decoder instance: " << result << std::endl; return -1; } - vulkanVideoProcessor->Initialize(&vkDevCtxt, videoStreamDemuxer, decoderConfig); + + VkSharedBaseObj frameToFile; + if (!decoderConfig.outputFileName.empty()) { + const char* crcOutputFile = decoderConfig.outputcrcPerFrame ? decoderConfig.crcOutputFileName.c_str() : nullptr; + result = VkVideoFrameToFile::Create(decoderConfig.outputFileName.c_str(), + decoderConfig.outputy4m, + decoderConfig.outputcrcPerFrame, + crcOutputFile, + decoderConfig.crcInitValue, + frameToFile); + if (result != VK_SUCCESS) { + fprintf(stderr, "Error creating output file %s\n", decoderConfig.outputFileName.c_str()); + return -1; + } + } + + vulkanVideoProcessor->Initialize(&vkDevCtxt, videoStreamDemuxer, frameToFile, decoderConfig); VkSharedBaseObj> videoQueue(vulkanVideoProcessor); DecoderFrameProcessorState frameProcessor(&vkDevCtxt, videoQueue, decoderConfig.decoderQueueSize); @@ -230,18 +252,5 @@ int main(int argc, const char **argv) { } while (continueLoop); } - if (decoderConfig.outputcrc != 0) { - fprintf(decoderConfig.crcOutputFile, "CRC: "); - for (size_t i = 0; i < decoderConfig.crcInitValue.size(); i += 1) { - fprintf(decoderConfig.crcOutputFile, "0x%08X ", crcAllocation[i]); - } - - fprintf(decoderConfig.crcOutputFile, "\n"); - if (decoderConfig.crcOutputFile != stdout) { - fclose(decoderConfig.crcOutputFile); - decoderConfig.crcOutputFile = stdout; - } - } - return 0; } diff --git a/vk_video_decoder/include/vulkan_video_decoder.h b/vk_video_decoder/include/vulkan_video_decoder.h index c3f2b02..9425f0b 100644 --- a/vk_video_decoder/include/vulkan_video_decoder.h +++ b/vk_video_decoder/include/vulkan_video_decoder.h @@ -41,6 +41,7 @@ #include "VkCodecUtils/VkVideoQueue.h" #include "VkCodecUtils/VulkanDecodedFrame.h" #include "VkDecoderUtils/VideoStreamDemuxer.h" +#include "VkCodecUtils/VkVideoFrameToFile.h" // High-level interface of the video decoder class VulkanVideoDecoder : public VkVideoQueue { @@ -87,6 +88,7 @@ class VulkanVideoDecoder : public VkVideoQueue { extern "C" VK_VIDEO_DECODER_EXPORT VkResult CreateVulkanVideoDecoder(VkInstance vkInstance, VkPhysicalDevice vkPhysicalDevice, VkDevice vkDevice, VkSharedBaseObj& videoStreamDemuxer, + VkSharedBaseObj& frameToFile, int argc, const char** argv, VkSharedBaseObj& vulkanVideoDecoder); diff --git a/vk_video_decoder/libs/CMakeLists.txt b/vk_video_decoder/libs/CMakeLists.txt index 5b03c49..bced917 100644 --- a/vk_video_decoder/libs/CMakeLists.txt +++ b/vk_video_decoder/libs/CMakeLists.txt @@ -50,7 +50,6 @@ set(LIBVKVIDEODECODER_SRC ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/nvVkFormats.cpp ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanBistreamBufferImpl.h ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanBistreamBufferImpl.cpp - ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/crcgenerator.cpp ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanCommandBufferPool.cpp ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanCommandBufferPool.h ${VK_VIDEO_DECODER_LIBS_SOURCE_ROOT}/VkDecoderUtils/VideoStreamDemuxer.h diff --git a/vk_video_decoder/src/vulkan_video_decoder.cpp b/vk_video_decoder/src/vulkan_video_decoder.cpp index f53c5e4..66bd265 100644 --- a/vk_video_decoder/src/vulkan_video_decoder.cpp +++ b/vk_video_decoder/src/vulkan_video_decoder.cpp @@ -88,6 +88,7 @@ class VulkanVideoDecoderImpl : public VulkanVideoDecoder { VkResult Initialize(VkInstance vkInstance, VkPhysicalDevice vkPhysicalDevice, VkDevice vkDevice, VkSharedBaseObj& videoStreamDemuxer, + VkSharedBaseObj& frameToFile, int argc, const char** argv); virtual ~VulkanVideoDecoderImpl() { } @@ -131,23 +132,13 @@ class VulkanVideoDecoderImpl : public VulkanVideoDecoder { VkResult VulkanVideoDecoderImpl::Initialize(VkInstance vkInstance, VkPhysicalDevice vkPhysicalDevice, VkDevice vkDevice, VkSharedBaseObj& videoStreamDemuxer, + VkSharedBaseObj& frameToFile, int argc, const char** argv) { const bool libraryMode = true; m_decoderConfig.ParseArgs(argc, argv); - // In the regular application use case the CRC output variables are allocated here and also output as part of main. - // In the library case it is up to the caller of the library to allocate the values and initialize them. - std::vector crcAllocation; - crcAllocation.resize(m_decoderConfig.crcInitValue.size()); - if (crcAllocation.empty() == false) { - m_decoderConfig.crcOutput = &crcAllocation[0]; - for (size_t i = 0; i < m_decoderConfig.crcInitValue.size(); i += 1) { - crcAllocation[i] = m_decoderConfig.crcInitValue[i]; - } - } - VkResult result = m_vkDevCtxt.InitVulkanDecoderDevice(m_decoderConfig.appName.c_str(), vkInstance, !m_decoderConfig.noPresent, @@ -253,7 +244,7 @@ VkResult VulkanVideoDecoderImpl::Initialize(VkInstance vkInstance, VkPhysicalDev return result; } - m_vulkanVideoProcessor->Initialize(&m_vkDevCtxt, videoStreamDemuxer, m_decoderConfig); + m_vulkanVideoProcessor->Initialize(&m_vkDevCtxt, videoStreamDemuxer, frameToFile, m_decoderConfig); if (!libraryMode) { @@ -311,7 +302,7 @@ VkResult VulkanVideoDecoderImpl::Initialize(VkInstance vkInstance, VkPhysicalDev return result; } - m_vulkanVideoProcessor->Initialize(&m_vkDevCtxt, videoStreamDemuxer, m_decoderConfig); + m_vulkanVideoProcessor->Initialize(&m_vkDevCtxt, videoStreamDemuxer, frameToFile, m_decoderConfig); if (!libraryMode) { @@ -325,25 +316,13 @@ VkResult VulkanVideoDecoderImpl::Initialize(VkInstance vkInstance, VkPhysicalDev } } - if (m_decoderConfig.outputcrc != 0) { - fprintf(m_decoderConfig.crcOutputFile, "CRC: "); - for (size_t i = 0; i < m_decoderConfig.crcInitValue.size(); i += 1) { - fprintf(m_decoderConfig.crcOutputFile, "0x%08X ", crcAllocation[i]); - } - - fprintf(m_decoderConfig.crcOutputFile, "\n"); - if (m_decoderConfig.crcOutputFile != stdout) { - fclose(m_decoderConfig.crcOutputFile); - m_decoderConfig.crcOutputFile = stdout; - } - } - return result; } VK_VIDEO_DECODER_EXPORT VkResult CreateVulkanVideoDecoder(VkInstance vkInstance, VkPhysicalDevice vkPhysicalDevice, VkDevice vkDevice, VkSharedBaseObj& videoStreamDemuxer, + VkSharedBaseObj& frameToFile, int argc, const char** argv, VkSharedBaseObj& vulkanVideoDecoder) { @@ -368,7 +347,7 @@ VkResult CreateVulkanVideoDecoder(VkInstance vkInstance, VkPhysicalDevice vkPhys return VK_ERROR_OUT_OF_HOST_MEMORY; } - VkResult result = vulkanVideoDecoderObj->Initialize(vkInstance, vkPhysicalDevice, vkDevice, videoStreamDemuxer, argc, argv); + VkResult result = vulkanVideoDecoderObj->Initialize(vkInstance, vkPhysicalDevice, vkDevice, videoStreamDemuxer, frameToFile, argc, argv); if (result != VK_SUCCESS) { vulkanVideoDecoderObj = nullptr; } else { diff --git a/vk_video_decoder/test/vulkan-video-dec/CMakeLists.txt b/vk_video_decoder/test/vulkan-video-dec/CMakeLists.txt index feb64ba..749a7b8 100644 --- a/vk_video_decoder/test/vulkan-video-dec/CMakeLists.txt +++ b/vk_video_decoder/test/vulkan-video-dec/CMakeLists.txt @@ -29,6 +29,7 @@ set(VULKAN_VIDEO_DEC_SOURCES ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanSamplerYcbcrConversion.cpp ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanSamplerYcbcrConversion.h ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/nvVkFormats.cpp + ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VkVideoFrameToFile.cpp ${VK_VIDEO_DECODER_LIBS_SOURCE_ROOT}/VkDecoderUtils/VideoStreamDemuxer.cpp ${VK_VIDEO_DECODER_LIBS_SOURCE_ROOT}/VkDecoderUtils/VideoStreamDemuxer.h ${VK_VIDEO_DECODER_LIBS_SOURCE_ROOT}/VkDecoderUtils/ElementaryStream.cpp diff --git a/vk_video_decoder/test/vulkan-video-dec/Main.cpp b/vk_video_decoder/test/vulkan-video-dec/Main.cpp index e53a598..0d1d7b0 100644 --- a/vk_video_decoder/test/vulkan-video-dec/Main.cpp +++ b/vk_video_decoder/test/vulkan-video-dec/Main.cpp @@ -123,7 +123,6 @@ int main(int argc, const char** argv) VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR | VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR)); if (result != VK_SUCCESS) { - assert(!"Can't initialize the Vulkan physical device!"); return -1; } @@ -149,18 +148,35 @@ int main(int argc, const char** argv) videoStreamDemuxer); if (result != VK_SUCCESS) { - assert(!"Can't initialize the VideoStreamDemuxer!"); return result; } - VkSharedBaseObj vulkanVideoDecoder; - result = CreateVulkanVideoDecoder(vkDevCtxt.getInstance(), vkDevCtxt.getPhysicalDevice(), vkDevCtxt.getDevice(), - videoStreamDemuxer, - argc, argv, vulkanVideoDecoder); + VkSharedBaseObj frameToFile; + if (!decoderConfig.outputFileName.empty()) { + const char* crcOutputFile = decoderConfig.outputcrcPerFrame ? decoderConfig.crcOutputFileName.c_str() : nullptr; + result = VkVideoFrameToFile::Create(decoderConfig.outputFileName.c_str(), + decoderConfig.outputy4m, + decoderConfig.outputcrcPerFrame, + crcOutputFile, + decoderConfig.crcInitValue, + frameToFile); + if (result != VK_SUCCESS) { + fprintf(stderr, "Error creating output file %s\n", decoderConfig.outputFileName.c_str()); + return -1; + } + } + VkSharedBaseObj vulkanVideoDecoder; + result = CreateVulkanVideoDecoder(vkDevCtxt.getInstance(), + vkDevCtxt.getPhysicalDevice(), + vkDevCtxt.getDevice(), + videoStreamDemuxer, + frameToFile, + argc, argv, + vulkanVideoDecoder); if (result != VK_SUCCESS) { - std::cerr << "Error creating the decoder instance: " << result << std::endl; + fprintf(stderr, "Error creating video decoder\n"); return -1; } @@ -170,7 +186,6 @@ int main(int argc, const char** argv) DecoderFrameProcessorState frameProcessor(&vkDevCtxt, videoQueue, 0); displayShell->AttachFrameProcessor(frameProcessor); - displayShell->RunLoop(); } else { @@ -183,12 +198,10 @@ int main(int argc, const char** argv) nullptr, requestVideoDecodeQueueMask); if (result != VK_SUCCESS) { - assert(!"Can't initialize the Vulkan physical device!"); return -1; } - result = vkDevCtxt.CreateVulkanDevice(numDecodeQueues, 0, // num encode queues videoCodecs, @@ -201,7 +214,6 @@ int main(int argc, const char** argv) requestVideoComputeQueueMask != 0 // createComputeQueue ); if (result != VK_SUCCESS) { - assert(!"Failed to create Vulkan device!"); return -1; } @@ -216,18 +228,35 @@ int main(int argc, const char** argv) videoStreamDemuxer); if (result != VK_SUCCESS) { - assert(!"Can't initialize the VideoStreamDemuxer!"); return result; } - VkSharedBaseObj vulkanVideoDecoder; - result = CreateVulkanVideoDecoder(vkDevCtxt.getInstance(), vkDevCtxt.getPhysicalDevice(), vkDevCtxt.getDevice(), - videoStreamDemuxer, - argc, argv, vulkanVideoDecoder); + VkSharedBaseObj frameToFile; + if (!decoderConfig.outputFileName.empty()) { + const char* crcOutputFile = decoderConfig.outputcrcPerFrame ? decoderConfig.crcOutputFileName.c_str() : nullptr; + result = VkVideoFrameToFile::Create(decoderConfig.outputFileName.c_str(), + decoderConfig.outputy4m, + decoderConfig.outputcrcPerFrame, + crcOutputFile, + decoderConfig.crcInitValue, + frameToFile); + if (result != VK_SUCCESS) { + fprintf(stderr, "Error creating output file %s\n", decoderConfig.outputFileName.c_str()); + return -1; + } + } + VkSharedBaseObj vulkanVideoDecoder; + result = CreateVulkanVideoDecoder(vkDevCtxt.getInstance(), + vkDevCtxt.getPhysicalDevice(), + vkDevCtxt.getDevice(), + videoStreamDemuxer, + frameToFile, + argc, argv, + vulkanVideoDecoder); if (result != VK_SUCCESS) { - std::cerr << "Error creating the decoder instance: " << result << std::endl; + fprintf(stderr, "Error creating video decoder\n"); return -1; } diff --git a/vk_video_encoder/demos/vk-video-enc/CMakeLists.txt b/vk_video_encoder/demos/vk-video-enc/CMakeLists.txt index 5697ae9..13a0f96 100644 --- a/vk_video_encoder/demos/vk-video-enc/CMakeLists.txt +++ b/vk_video_encoder/demos/vk-video-enc/CMakeLists.txt @@ -82,7 +82,7 @@ set(sources ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/nvVkFormats.cpp ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanBistreamBufferImpl.h ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VulkanBistreamBufferImpl.cpp - ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/crcgenerator.cpp + ${VK_VIDEO_COMMON_LIBS_SOURCE_ROOT}/VkCodecUtils/VkVideoFrameToFile.cpp ${VK_VIDEO_DECODER_LIBS_SOURCE_ROOT}/VkDecoderUtils/VideoStreamDemuxer.cpp ${VK_VIDEO_DECODER_LIBS_SOURCE_ROOT}/VkDecoderUtils/VideoStreamDemuxer.h ${VK_VIDEO_DECODER_LIBS_SOURCE_ROOT}/VkDecoderUtils/ElementaryStream.cpp