diff --git a/README.md b/README.md index 393e58b5..3735155a 100644 --- a/README.md +++ b/README.md @@ -133,3 +133,7 @@ python3 tests/video_test_framework_encode.py --test "encode_h264_*" ``` For complete documentation, command-line options, and advanced usage examples, see **[tests/README.md](tests/README.md)**. + +## Contributing + +We welcome contributions! Please see [CONTRIBUTING](CONTRIBUTING) for guidelines on commit messages, pull requests, and testing requirements. diff --git a/common/include/VkVSCommon.h b/common/include/VkVSCommon.h index 704ec1fb..f88a401d 100644 --- a/common/include/VkVSCommon.h +++ b/common/include/VkVSCommon.h @@ -78,14 +78,17 @@ inline const char* string_VkResult_Extended(VkResult result) { } // Helper function to check if a VkResult indicates video profile/feature not supported -// Returns true if the error indicates the hardware/driver doesn't support the requested -// video codec profile or feature (as opposed to an actual runtime error) -inline bool IsVideoProfileNotSupportedError(VkResult result) { +// Returns true for video-specific KHR errors (profile, format, codec, std version) +// and general Vulkan capability errors (format, feature, driver, extension) +inline bool IsVideoUnsupportedResult(VkResult result) { return result == VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR || result == VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR || result == VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR || + result == VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR || result == VK_ERROR_FORMAT_NOT_SUPPORTED || - result == VK_ERROR_FEATURE_NOT_PRESENT; + result == VK_ERROR_FEATURE_NOT_PRESENT || + result == VK_ERROR_INCOMPATIBLE_DRIVER || + result == VK_ERROR_EXTENSION_NOT_PRESENT; } #endif // __cplusplus diff --git a/common/libs/VkCodecUtils/VkVideoFrameOutput.h b/common/libs/VkCodecUtils/VkVideoFrameOutput.h index 2e253cda..b0cbe980 100644 --- a/common/libs/VkCodecUtils/VkVideoFrameOutput.h +++ b/common/libs/VkCodecUtils/VkVideoFrameOutput.h @@ -62,9 +62,10 @@ class VkVideoFrameOutput : public VkVideoRefCountBase { * * @param pFrame Pointer to the decoded frame * @param vkDevCtx Vulkan device context - * @return size_t Number of bytes written, (size_t)-1 on error + * @param bytesWritten Optional pointer to receive number of bytes written + * @return bool true on success, false on error */ - virtual size_t OutputFrame(VulkanDecodedFrame* pFrame, const VulkanDeviceContext* vkDevCtx) = 0; + virtual bool OutputFrame(VulkanDecodedFrame* pFrame, const VulkanDeviceContext* vkDevCtx, size_t* bytesWritten = nullptr) = 0; protected: VkVideoFrameOutput() = default; diff --git a/common/libs/VkCodecUtils/VkVideoFrameToFile.cpp b/common/libs/VkCodecUtils/VkVideoFrameToFile.cpp index 57c5c979..1ea63e91 100644 --- a/common/libs/VkCodecUtils/VkVideoFrameToFile.cpp +++ b/common/libs/VkCodecUtils/VkVideoFrameToFile.cpp @@ -189,9 +189,14 @@ class VkVideoFrameToFileImpl : public VkVideoFrameOutput { return ret; } - virtual size_t OutputFrame(VulkanDecodedFrame* pFrame, const VulkanDeviceContext* vkDevCtx) override { + virtual bool OutputFrame(VulkanDecodedFrame* pFrame, const VulkanDeviceContext* vkDevCtx, size_t* bytesWritten = nullptr) override { + if (bytesWritten) { + *bytesWritten = 0; + } + if (!IsFileStreamValid()) { - return (size_t)-1; + std::cerr << "ERROR: Output file stream is not valid\n"; + return false; } assert(pFrame != nullptr); @@ -218,6 +223,10 @@ class VkVideoFrameToFileImpl : public VkVideoFrameOutput { const VkMpFormatInfo* mpInfo = YcbcrVkFormatInfo(format); size_t usedBufferSize = ConvertFrameToNv12(vkDevCtx, pFrame->displayWidth, pFrame->displayHeight, imageResource, pOutputBuffer, mpInfo); + if (usedBufferSize == 0) { + std::cerr << "ERROR: Failed to convert frame to NV12. Output size is 0\n"; + return false; + } if (m_outputcrcPerFrame && m_crcOutputFile) { fprintf(m_crcOutputFile, "CRC Frame[%" PRId64 "]:", pFrame->displayOrder); @@ -239,9 +248,9 @@ class VkVideoFrameToFileImpl : public VkVideoFrameOutput { } if (m_outputy4m) { - return WriteFrameToFileY4M(0, usedBufferSize, pFrame->displayWidth, pFrame->displayHeight, mpInfo); + return WriteFrameToFileY4M(0, usedBufferSize, pFrame->displayWidth, pFrame->displayHeight, mpInfo, bytesWritten); } else { - return WriteDataToFile(0, usedBufferSize); + return WriteDataToFile(0, usedBufferSize, bytesWritten); } } @@ -269,53 +278,93 @@ class VkVideoFrameToFileImpl : public VkVideoFrameOutput { return IsFileStreamValid(); } - size_t WriteDataToFile(size_t offset, size_t size) { - return fwrite(m_pLinearMemory + offset, size, 1, m_outputFile); + bool WriteDataToFile(size_t offset, size_t size, size_t* bytesWritten) { + size_t totalBytesWritten = 0; + while (totalBytesWritten < size) { + size_t remainingBytes = size - totalBytesWritten; + size_t written = fwrite(m_pLinearMemory + offset + totalBytesWritten, 1, remainingBytes, m_outputFile); + if (ferror(m_outputFile)) { + fprintf(stderr, "ERROR: fwrite failed (wrote %zu of %zu bytes)\n", + totalBytesWritten + written, size); + if (bytesWritten) { + *bytesWritten = totalBytesWritten + written; + } + return false; + } + if (written == 0) { + fprintf(stderr, "ERROR: fwrite wrote 0 bytes unexpectedly (wrote %zu of %zu bytes)\n", + totalBytesWritten, size); + if (bytesWritten) { + *bytesWritten = totalBytesWritten; + } + return false; + } + totalBytesWritten += written; + } + if (bytesWritten) { + *bytesWritten = totalBytesWritten; + } + return true; } size_t GetMaxFrameSize() { return m_allocationSize; } - size_t WriteFrameToFileY4M(size_t offset, size_t size, size_t width, size_t height, - const VkMpFormatInfo* mpInfo) { + bool WriteFrameToFileY4M(size_t offset, size_t size, size_t width, size_t height, + const VkMpFormatInfo* mpInfo, size_t* bytesWritten) { + + auto setErrorAndReturn = [&bytesWritten]() { + if (bytesWritten) { + *bytesWritten = 0; + } + return false; + }; + + if (mpInfo == nullptr) { + fprintf(stderr, "ERROR: mpInfo is required for Y4M output\n"); + return setErrorAndReturn();; + } + 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"); - } + const char* chromaFormat = mpInfo->planesLayout.secondaryPlaneSubsampledX ? "C420" : "C444"; + const char* bitDepth = (mpInfo->planesLayout.bpp != YCBCRA_8BPP) ? "p16" : ""; - fprintf(m_outputFile, "\n"); + if (fprintf(m_outputFile, "YUV4MPEG2 W%i H%i F24:1 Ip A1:1 %s%s\n", + (int)width, (int)height, chromaFormat, bitDepth) < 0) { + fprintf(stderr, "ERROR: fprintf failed writing Y4M header\n"); + return setErrorAndReturn(); + } } - 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); + if (fprintf(m_outputFile, "FRAME W%i H%i\n", (int)width, (int)height) < 0) { + fprintf(stderr, "ERROR: fprintf failed writing Y4M frame header\n"); + return setErrorAndReturn(); + } m_height = height; m_width = width; + } else { + if (fprintf(m_outputFile, "FRAME\n") < 0) { + fprintf(stderr, "ERROR: fprintf failed writing Y4M frame marker\n"); + return setErrorAndReturn(); + } } - fprintf(m_outputFile, "\n"); - return WriteDataToFile(offset, size); + return WriteDataToFile(offset, size, bytesWritten); } size_t ConvertFrameToNv12(const VulkanDeviceContext* vkDevCtx, int32_t frameWidth, int32_t frameHeight, VkSharedBaseObj& imageResource, uint8_t* pOutBuffer, const VkMpFormatInfo* mpInfo) { + if (mpInfo == nullptr) { + fprintf(stderr, "ERROR: mpInfo is required for NV12 conversion. Unable to convert frame, return 0.\n"); + return 0; + } size_t outputBufferSize = 0; VkDevice device = imageResource->GetDevice(); VkImage srcImage = imageResource->GetImage(); @@ -331,21 +380,21 @@ class VkVideoFrameToFileImpl : public VkVideoFrameOutput { int32_t secondaryPlaneHeight = frameHeight; int32_t imageHeight = frameHeight; bool isUnnormalizedRgba = false; - if (mpInfo && (mpInfo->planesLayout.layout == YCBCR_SINGLE_PLANE_UNNORMALIZED) && !(mpInfo->planesLayout.disjoint)) { + if ((mpInfo->planesLayout.layout == YCBCR_SINGLE_PLANE_UNNORMALIZED) && !(mpInfo->planesLayout.disjoint)) { isUnnormalizedRgba = true; } - if (mpInfo && mpInfo->planesLayout.secondaryPlaneSubsampledX) { + if (mpInfo->planesLayout.secondaryPlaneSubsampledX) { secondaryPlaneWidth = (secondaryPlaneWidth + 1) / 2; } - if (mpInfo && mpInfo->planesLayout.secondaryPlaneSubsampledY) { + if (mpInfo->planesLayout.secondaryPlaneSubsampledY) { secondaryPlaneHeight = (secondaryPlaneHeight + 1) / 2; } VkImageSubresource subResource = {}; VkSubresourceLayout layouts[3] = {}; - if (mpInfo && !isUnnormalizedRgba) { + if (!isUnnormalizedRgba) { switch (mpInfo->planesLayout.layout) { case YCBCR_SINGLE_PLANE_UNNORMALIZED: case YCBCR_SINGLE_PLANE_INTERLEAVED: diff --git a/common/libs/VkCodecUtils/VkVideoQueue.h b/common/libs/VkCodecUtils/VkVideoQueue.h index 37e77f3d..67a5e317 100644 --- a/common/libs/VkCodecUtils/VkVideoQueue.h +++ b/common/libs/VkCodecUtils/VkVideoQueue.h @@ -20,6 +20,17 @@ #include #include "VkCodecUtils/VkVideoRefCountBase.h" +/** + * @enum VkVideoQueueResult + * @brief Result codes for VkVideoQueue::GetNextFrame operations. + */ +enum class VkVideoQueueResult { + NewFrame, + NoFrame, + EndOfStream, + Error +}; + /** * @class VkVideoQueue * @brief Interface for retrieving frames from a Vulkan-based video queue. @@ -137,17 +148,15 @@ class VkVideoQueue : public VkVideoRefCountBase { * information about the newly decoded frame, if available. * If no frame is currently available or it was the end of the stream, * there will be no data filled in pNewFrame. - * @param[out] endOfStream Set to `true` if the end of the stream has been reached (i.e., no more - * frames will ever be available), or `false` otherwise. * * @return - * - `1` if a decoded frame is successfully retrieved. - * - `0` if no frame is currently available (but not an error, you may need to parse more data or - * wait for pending frames to be reordered because of the B-frames are present). - * - `-1` if an error occurs or if the end of the stream is reached. In either case, decoding should - * be terminated or reset accordingly. + * - `VkVideoQueueResult::NewFrame` if a decoded frame is successfully retrieved. + * - `VkVideoQueueResult::NoFrame` if no frame is currently available (need more parsing + * or B-frame reordering). + * - `VkVideoQueueResult::EndOfStream` if the end of stream has been reached. + * - `VkVideoQueueResult::Error` if an error occurs during frame retrieval. */ - virtual int32_t GetNextFrame(FrameDataType* pNewFrame, bool* endOfStream) = 0; + virtual VkVideoQueueResult GetNextFrame(FrameDataType* pNewFrame) = 0; /** * @brief Release a previously retrieved decoded frame. diff --git a/common/libs/VkCodecUtils/VulkanFrame.cpp b/common/libs/VkCodecUtils/VulkanFrame.cpp index e1f3d910..223affdf 100644 --- a/common/libs/VkCodecUtils/VulkanFrame.cpp +++ b/common/libs/VkCodecUtils/VulkanFrame.cpp @@ -359,17 +359,16 @@ bool VulkanFrame::OnFrame( int32_t renderIndex, pLastDecodedFrame->Reset(); - bool endOfStream = false; - int32_t numVideoFrames = 0; - - numVideoFrames = m_videoQueue->GetNextFrame(pLastDecodedFrame, &endOfStream); - if (endOfStream && (numVideoFrames < 0)) { + VkVideoQueueResult result = m_videoQueue->GetNextFrame(pLastDecodedFrame); + if (result == VkVideoQueueResult::EndOfStream || result == VkVideoQueueResult::Error) { continueLoop = false; bool displayTimeNow = true; float fps = GetFrameRateFps(displayTimeNow); if (displayTimeNow) { std::cout << "\t\tFrame " << m_frameCount << ", FPS: " << fps << std::endl; } + } else if (dumpDebug && result == VkVideoQueueResult::NoFrame) { + std::cout << "No frame available, waiting for more data" << std::endl; } } diff --git a/common/libs/VkCodecUtils/VulkanVideoDisplayQueue.h b/common/libs/VkCodecUtils/VulkanVideoDisplayQueue.h index 098c5c33..cabbd5e0 100644 --- a/common/libs/VkCodecUtils/VulkanVideoDisplayQueue.h +++ b/common/libs/VkCodecUtils/VulkanVideoDisplayQueue.h @@ -39,7 +39,7 @@ class VulkanVideoDisplayQueue : public VkVideoQueue { virtual uint32_t GetProfileIdc() const { return 0; } virtual VkExtent3D GetVideoExtent() const; - virtual int32_t GetNextFrame(FrameDataType* pFrame, bool* endOfStream); + virtual VkVideoQueueResult GetNextFrame(FrameDataType* pFrame); virtual int32_t ReleaseFrame(FrameDataType* pDisplayedFrame); static VkSharedBaseObj& invalidVulkanVideoDisplayQueue; @@ -175,20 +175,23 @@ int32_t VulkanVideoDisplayQueue::EnqueueFrame(FrameDataType* pFra } template -int32_t VulkanVideoDisplayQueue::GetNextFrame(FrameDataType* pFrame, bool* endOfStream) +VkVideoQueueResult VulkanVideoDisplayQueue::GetNextFrame(FrameDataType* pFrame) { if (m_exitQueueRequested) { m_queue.SetFlushAndExit(); m_queueIsEnabled = false; } - *endOfStream = !m_queue.WaitAndPop(*pFrame) && !m_queueIsEnabled; + bool gotFrame = m_queue.WaitAndPop(*pFrame); - if (*endOfStream) { - return -1; + if (!gotFrame) { + if (!m_queueIsEnabled) { + return VkVideoQueueResult::EndOfStream; + } + return VkVideoQueueResult::NoFrame; } - return 1; + return VkVideoQueueResult::NewFrame; } template diff --git a/common/libs/VkCodecUtils/VulkanVideoProcessor.cpp b/common/libs/VkCodecUtils/VulkanVideoProcessor.cpp index c4408f03..f134ded7 100644 --- a/common/libs/VkCodecUtils/VulkanVideoProcessor.cpp +++ b/common/libs/VkCodecUtils/VulkanVideoProcessor.cpp @@ -35,15 +35,14 @@ #include "nvidia_utils/vulkan/ycbcrvkinfo.h" #include "crcgenerator.h" -int32_t VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, - VkSharedBaseObj& videoStreamDemuxer, - VkSharedBaseObj& frameToFile, - DecoderConfig& programConfig) +VkResult VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, + VkSharedBaseObj& videoStreamDemuxer, + VkSharedBaseObj& frameToFile, + DecoderConfig& programConfig) { int32_t videoQueueIndx = programConfig.queueId; const uint32_t loopCount = programConfig.loopCount; - const uint32_t startFrame = 0; const int32_t maxFrameCount = programConfig.maxFrameCount; const int32_t numDecodeImagesInFlight = std::max(programConfig.numDecodeImagesInFlight, 4); const int32_t numDecodeImagesToPreallocate = programConfig.numDecodeImagesToPreallocate; @@ -59,8 +58,7 @@ int32_t VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, if (vkDevCtx->GetVideoDecodeQueue(videoQueueIndx) == VkQueue()) { std::cerr << "videoQueueIndx is out of bounds: " << videoQueueIndx << " Max decode queues: " << vkDevCtx->GetVideoDecodeNumQueues() << std::endl; - assert(!"Invalid Video Queue"); - return -1; + return VK_ERROR_FEATURE_NOT_PRESENT; } Deinit(); @@ -79,9 +77,9 @@ int32_t VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, } VkResult result = VulkanVideoFrameBuffer::Create(vkDevCtx, m_vkVideoFrameBuffer); - assert(result == VK_SUCCESS); if (result != VK_SUCCESS) { fprintf(stderr, "\nERROR: Create VulkanVideoFrameBuffer result: 0x%x\n", result); + return result; } m_frameToFile = frameToFile; @@ -112,9 +110,9 @@ int32_t VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, numDecodeImagesToPreallocate, numBitstreamBuffersToPreallocate, m_vkVideoDecoder); - assert(result == VK_SUCCESS); if (result != VK_SUCCESS) { fprintf(stderr, "\nERROR: Create VkVideoDecoder result: 0x%x\n", result); + return result; } VkVideoCoreProfile videoProfile(m_videoStreamDemuxer->GetVideoCodec(), @@ -127,8 +125,7 @@ int32_t VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, vkDevCtx->GetVideoDecodeQueueFamilyIdx(), m_videoStreamDemuxer->GetVideoCodec())) { std::cout << "*** The video codec " << VkVideoCoreProfile::CodecToName(m_videoStreamDemuxer->GetVideoCodec()) << " is not supported! ***" << std::endl; - assert(!"The video codec is not supported"); - return -1; + return VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR; } VkVideoCapabilitiesKHR videoCapabilities; @@ -140,8 +137,7 @@ int32_t VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, if (result != VK_SUCCESS) { std::cerr << "FATAL ERROR: Video decode capabilities not supported for this codec/profile!" << std::endl; std::cerr << "GetVideoDecodeCapabilities failed with error: " << string_VkResult_Extended(result) << std::endl; - // Return negated VkResult to preserve error information for caller - return -static_cast(result); + return result; } // Validate video dimensions against hardware capabilities @@ -154,8 +150,7 @@ int32_t VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, << " is outside supported range [" << videoCapabilities.minCodedExtent.width << ", " << videoCapabilities.maxCodedExtent.width << "] ***" << std::endl; - // Return negated VK_ERROR_FORMAT_NOT_SUPPORTED to indicate unsupported dimensions - return -static_cast(VK_ERROR_FORMAT_NOT_SUPPORTED); + return VK_ERROR_FORMAT_NOT_SUPPORTED; } if (videoHeight < videoCapabilities.minCodedExtent.height || @@ -164,8 +159,7 @@ int32_t VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, << " is outside supported range [" << videoCapabilities.minCodedExtent.height << ", " << videoCapabilities.maxCodedExtent.height << "] ***" << std::endl; - // Return negated VK_ERROR_FORMAT_NOT_SUPPORTED to indicate unsupported dimensions - return -static_cast(VK_ERROR_FORMAT_NOT_SUPPORTED); + return VK_ERROR_FORMAT_NOT_SUPPORTED; } const uint32_t defaultMinBufferSize = 2 * 1024 * 1024; // 2MB @@ -174,16 +168,16 @@ int32_t VulkanVideoProcessor::Initialize(const VulkanDeviceContext* vkDevCtx, defaultMinBufferSize, (uint32_t)videoCapabilities.minBitstreamBufferOffsetAlignment, (uint32_t)videoCapabilities.minBitstreamBufferSizeAlignment); - assert(result == VK_SUCCESS); if (result != VK_SUCCESS) { fprintf(stderr, "\nERROR: CreateParser() result: 0x%x\n", result); + return result; } m_loopCount = loopCount; - m_startFrame = startFrame; + m_startFrame = 0; m_maxFrameCount = maxFrameCount; - return 0; + return VK_SUCCESS; } VkResult VulkanVideoProcessor::Create(const DecoderConfig& settings, const VulkanDeviceContext* vkDevCtx, @@ -422,13 +416,17 @@ void VulkanVideoProcessor::DumpVideoFormat(const VkParserDetectedVideoFormat* vi } } -size_t VulkanVideoProcessor::OutputFrameToFile(VulkanDecodedFrame* pFrame) +bool VulkanVideoProcessor::OutputFrameToFile(VulkanDecodedFrame* pFrame, size_t* bytesWritten) { if (!m_frameToFile) { - return (size_t)-1; + std::cerr << "ERROR: Frame to file is not set\n"; + if (bytesWritten) { + *bytesWritten = 0; + } + return false; } - return m_frameToFile->OutputFrame(pFrame, m_vkDevCtx); + return m_frameToFile->OutputFrame(pFrame, m_vkDevCtx, bytesWritten); } uint32_t VulkanVideoProcessor::Restart(int64_t& bitstreamOffset) @@ -498,7 +496,7 @@ int32_t VulkanVideoProcessor::ParserProcessNextDataChunk() return retValue; } -int32_t VulkanVideoProcessor::GetNextFrame(VulkanDecodedFrame* pFrame, bool* endOfStream) +VkVideoQueueResult VulkanVideoProcessor::GetNextFrame(VulkanDecodedFrame* pFrame) { // The below call to DequeueDecodedPicture allows returning the next frame without parsing of the stream. // Parsing is only done when there are no more frames in the queue. @@ -519,7 +517,10 @@ int32_t VulkanVideoProcessor::GetNextFrame(VulkanDecodedFrame* pFrame, bool* end } if (m_frameToFile) { - OutputFrameToFile(pFrame); + if (!OutputFrameToFile(pFrame)) { + fprintf(stderr, "ERROR: Failed to output frame %u to file\n", m_videoFrameNum); + return VkVideoQueueResult::Error; + } } m_videoFrameNum++; @@ -530,17 +531,18 @@ int32_t VulkanVideoProcessor::GetNextFrame(VulkanDecodedFrame* pFrame, bool* end std::cout << "Number of video frames " << m_videoFrameNum << " of max frame number " << m_maxFrameCount << std::endl; m_videoStreamsCompleted = StreamCompleted(); - *endOfStream = m_videoStreamsCompleted; - return -1; + return VkVideoQueueResult::EndOfStream; } - *endOfStream = m_videoStreamsCompleted; - if ((framesInQueue == 0) && m_videoStreamsCompleted) { - return -1; + return VkVideoQueueResult::EndOfStream; + } + + if (framesInQueue == 0) { + return VkVideoQueueResult::NoFrame; } - return 1; + return VkVideoQueueResult::NewFrame; } int32_t VulkanVideoProcessor::ReleaseFrame(VulkanDecodedFrame* pDisplayedFrame) diff --git a/common/libs/VkCodecUtils/VulkanVideoProcessor.h b/common/libs/VkCodecUtils/VulkanVideoProcessor.h index 33986c90..cbdca1f1 100644 --- a/common/libs/VkCodecUtils/VulkanVideoProcessor.h +++ b/common/libs/VkCodecUtils/VulkanVideoProcessor.h @@ -33,7 +33,7 @@ class VulkanVideoProcessor : public VkVideoQueue { virtual uint32_t GetProfileIdc() const; virtual VkFormat GetFrameImageFormat() const; virtual VkExtent3D GetVideoExtent() const; - virtual int32_t GetNextFrame(VulkanDecodedFrame* pFrame, bool* endOfStream); + virtual VkVideoQueueResult GetNextFrame(VulkanDecodedFrame* pFrame); virtual int32_t ReleaseFrame(VulkanDecodedFrame* pDisplayedFrame); static VkSharedBaseObj& invalidVulkanVideoProcessor; @@ -41,10 +41,10 @@ class VulkanVideoProcessor : public VkVideoQueue { static VkResult Create(const DecoderConfig& settings, const VulkanDeviceContext* vkDevCtx, VkSharedBaseObj& vulkanVideoProcessor = invalidVulkanVideoProcessor); - int32_t Initialize(const VulkanDeviceContext* vkDevCtx, - VkSharedBaseObj& videoStreamDemuxer, - VkSharedBaseObj& frameToFile, - DecoderConfig& programConfig); + VkResult Initialize(const VulkanDeviceContext* vkDevCtx, + VkSharedBaseObj& videoStreamDemuxer, + VkSharedBaseObj& frameToFile, + DecoderConfig& programConfig); void Deinit(); @@ -67,7 +67,7 @@ class VulkanVideoProcessor : public VkVideoQueue { int32_t ParserProcessNextDataChunk(); - size_t OutputFrameToFile(VulkanDecodedFrame* pFrame); + bool OutputFrameToFile(VulkanDecodedFrame* pFrame, size_t* bytesWritten = nullptr); uint32_t Restart(int64_t& bitstreamOffset); private: diff --git a/vk_video_decoder/demos/vk-video-dec/Main.cpp b/vk_video_decoder/demos/vk-video-dec/Main.cpp index 062dbe51..4a087a87 100644 --- a/vk_video_decoder/demos/vk-video-dec/Main.cpp +++ b/vk_video_decoder/demos/vk-video-dec/Main.cpp @@ -155,11 +155,10 @@ int main(int argc, const char **argv) } } - int32_t initResult = vulkanVideoProcessor->Initialize(&vkDevCtxt, videoStreamDemuxer, frameToFile, decoderConfig); - if (initResult != 0) { + result = vulkanVideoProcessor->Initialize(&vkDevCtxt, videoStreamDemuxer, frameToFile, decoderConfig); + if (result != VK_SUCCESS) { fprintf(stderr, "Failed to initialize video processor\n"); - // Check if the error indicates "unsupported" (negated VkResult values) - if (initResult < 0 && IsVideoProfileNotSupportedError(static_cast(-initResult))) { + if (IsVideoUnsupportedResult(result)) { return VVS_EXIT_UNSUPPORTED; } return EXIT_FAILURE; @@ -241,11 +240,10 @@ int main(int argc, const char **argv) } } - int32_t initResult = vulkanVideoProcessor->Initialize(&vkDevCtxt, videoStreamDemuxer, frameToFile, decoderConfig); - if (initResult != 0) { + result = vulkanVideoProcessor->Initialize(&vkDevCtxt, videoStreamDemuxer, frameToFile, decoderConfig); + if (result != VK_SUCCESS) { fprintf(stderr, "Failed to initialize video processor\n"); - // Check if the error indicates "unsupported" (negated VkResult values) - if (initResult < 0 && IsVideoProfileNotSupportedError(static_cast(-initResult))) { + if (IsVideoUnsupportedResult(result)) { return VVS_EXIT_UNSUPPORTED; } return EXIT_FAILURE; diff --git a/vk_video_decoder/src/vulkan_video_decoder.cpp b/vk_video_decoder/src/vulkan_video_decoder.cpp index 2f3d5490..1d0e0541 100644 --- a/vk_video_decoder/src/vulkan_video_decoder.cpp +++ b/vk_video_decoder/src/vulkan_video_decoder.cpp @@ -44,9 +44,9 @@ class VulkanVideoDecoderImpl : public VulkanVideoDecoder { return m_vulkanVideoProcessor->GetFrameImageFormat(); } - virtual int32_t GetNextFrame(VulkanDecodedFrame* pNewDecodedFrame, bool* endOfStream) + virtual VkVideoQueueResult GetNextFrame(VulkanDecodedFrame* pNewDecodedFrame) { - return m_vulkanVideoProcessor->GetNextFrame(pNewDecodedFrame, endOfStream); + return m_vulkanVideoProcessor->GetNextFrame(pNewDecodedFrame); } virtual int32_t ReleaseFrame(VulkanDecodedFrame* pDoneDecodedFrame) @@ -222,14 +222,10 @@ VkResult VulkanVideoDecoderImpl::Initialize(VkInstance vkInstance, return result; } - int32_t initStatus = m_vulkanVideoProcessor->Initialize(&m_vkDevCtxt, + result = m_vulkanVideoProcessor->Initialize(&m_vkDevCtxt, videoStreamDemuxer, frameToFile, m_decoderConfig); - if (initStatus != 0) { - return VK_ERROR_INITIALIZATION_FAILED; - } - return result; } diff --git a/vk_video_decoder/test/vulkan-video-dec/Main.cpp b/vk_video_decoder/test/vulkan-video-dec/Main.cpp index 9093a359..e0df9de1 100644 --- a/vk_video_decoder/test/vulkan-video-dec/Main.cpp +++ b/vk_video_decoder/test/vulkan-video-dec/Main.cpp @@ -17,6 +17,7 @@ #include #include "VkCodecUtils/DecoderConfig.h" +#include "VkVSCommon.h" #include "vulkan_video_decoder.h" #include "VkVideoCore/VkVideoCoreProfile.h" #include "VkCodecUtils/VulkanFrame.h" @@ -77,6 +78,9 @@ int main(int argc, const char** argv) if (result != VK_SUCCESS) { printf("Could not initialize the Vulkan decoder device!\n"); + if (IsVideoUnsupportedResult(result)) { + return VVS_EXIT_UNSUPPORTED; + } return EXIT_FAILURE; } @@ -126,7 +130,10 @@ int main(int argc, const char** argv) decoderConfig.verbose, decoderConfig.noDeviceFallback); if (result != VK_SUCCESS) { - assert(!"Can't initialize the Vulkan physical device!"); + fprintf(stderr, "Can't initialize the Vulkan physical device!\n"); + if (IsVideoUnsupportedResult(result)) { + return VVS_EXIT_UNSUPPORTED; + } return EXIT_FAILURE; } assert(displayShell->PhysDeviceCanPresent(vkDevCtxt.getPhysicalDevice(), @@ -168,6 +175,9 @@ int main(int argc, const char** argv) vulkanVideoDecoder); if (result != VK_SUCCESS) { fprintf(stderr, "Error creating video decoder\n"); + if (IsVideoUnsupportedResult(result)) { + return VVS_EXIT_UNSUPPORTED; + } return EXIT_FAILURE; } @@ -194,7 +204,10 @@ int main(int argc, const char** argv) decoderConfig.verbose, decoderConfig.noDeviceFallback); if (result != VK_SUCCESS) { - assert(!"Can't initialize the Vulkan physical device!"); + fprintf(stderr, "Can't initialize the Vulkan physical device!\n"); + if (IsVideoUnsupportedResult(result)) { + return VVS_EXIT_UNSUPPORTED; + } return EXIT_FAILURE; } @@ -210,7 +223,10 @@ int main(int argc, const char** argv) requestVideoComputeQueueMask != 0 // createComputeQueue ); if (result != VK_SUCCESS) { - assert(!"Failed to create Vulkan device!"); + fprintf(stderr, "Failed to create Vulkan device!\n"); + if (IsVideoUnsupportedResult(result)) { + return VVS_EXIT_UNSUPPORTED; + } return EXIT_FAILURE; } @@ -240,6 +256,9 @@ int main(int argc, const char** argv) vulkanVideoDecoder); if (result != VK_SUCCESS) { fprintf(stderr, "Error creating video decoder\n"); + if (IsVideoUnsupportedResult(result)) { + return VVS_EXIT_UNSUPPORTED; + } return EXIT_FAILURE; } diff --git a/vk_video_decoder/test/vulkan-video-simple-dec/Main.cpp b/vk_video_decoder/test/vulkan-video-simple-dec/Main.cpp index 47d0071a..4428c7d8 100644 --- a/vk_video_decoder/test/vulkan-video-simple-dec/Main.cpp +++ b/vk_video_decoder/test/vulkan-video-simple-dec/Main.cpp @@ -49,6 +49,8 @@ static bool GetNextFrame(VkSharedBaseObj& vulkanVideoDecoder uint32_t& curFrameDataQueueIndex) { bool continueLoop = true; + bool gotFrame = false; + const bool dumpDebug = true; VulkanDecodedFrame& data = frameDataQueue[curFrameDataQueueIndex]; VulkanDecodedFrame* pLastDecodedFrame = nullptr; @@ -61,18 +63,20 @@ static bool GetNextFrame(VkSharedBaseObj& vulkanVideoDecoder pLastDecodedFrame->Reset(); - bool endOfStream = false; - int32_t numVideoFrames = 0; - - numVideoFrames = vulkanVideoDecoder->GetNextFrame(pLastDecodedFrame, &endOfStream); - if (endOfStream && (numVideoFrames < 0)) { + VkVideoQueueResult result = vulkanVideoDecoder->GetNextFrame(pLastDecodedFrame); + if (result == VkVideoQueueResult::EndOfStream || result == VkVideoQueueResult::Error) { continueLoop = false; + } else if (result == VkVideoQueueResult::NoFrame) { + if (dumpDebug) { + std::cout << "No frame available, waiting for more data" << std::endl; + } + } else { + gotFrame = true; } } // wait for the last submission since we reuse frame data - const bool dumpDebug = true; - if (dumpDebug && pLastDecodedFrame) { + if (dumpDebug && gotFrame && pLastDecodedFrame) { VkSharedBaseObj imageResourceView; pLastDecodedFrame->imageViews[VulkanDecodedFrame::IMAGE_VIEW_TYPE_OPTIMAL_DISPLAY].GetImageResourceView(imageResourceView); @@ -87,7 +91,9 @@ static bool GetNextFrame(VkSharedBaseObj& vulkanVideoDecoder << std::endl; } - curFrameDataQueueIndex = (curFrameDataQueueIndex + 1) % frameDataQueue.size(); + if (gotFrame) { + curFrameDataQueueIndex = (curFrameDataQueueIndex + 1) % frameDataQueue.size(); + } return continueLoop; } @@ -151,6 +157,9 @@ int main(int argc, const char** argv) vulkanVideoDecoder); if (result != VK_SUCCESS) { fprintf(stderr, "Error creating video decoder\n"); + if (IsVideoUnsupportedResult(result)) { + return VVS_EXIT_UNSUPPORTED; + } return EXIT_FAILURE; } diff --git a/vk_video_encoder/demos/vk-video-enc/Main.cpp b/vk_video_encoder/demos/vk-video-enc/Main.cpp index 7d367680..31d24b2d 100644 --- a/vk_video_encoder/demos/vk-video-enc/Main.cpp +++ b/vk_video_encoder/demos/vk-video-enc/Main.cpp @@ -268,8 +268,11 @@ int main(int argc, char** argv) (encoderConfig->enablePreprocessComputeFilter == VK_TRUE)) ); if (result != VK_SUCCESS) { - - assert(!"Failed to create Vulkan device!"); + if (IsVideoUnsupportedResult(result)) { + printf("Failed to create Vulkan device: unsupported feature\n"); + return VVS_EXIT_UNSUPPORTED; + } + printf("Failed to create Vulkan device!\n"); return EXIT_FAILURE; } diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp index 99686a98..0b845181 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp @@ -201,7 +201,7 @@ VkResult EncoderConfigAV1::InitDeviceCapabilities(const VulkanDeviceContext* vkD intraRefreshCapabilities); if (result != VK_SUCCESS) { // Distinguish between "not supported" and "actual error" - if (IsVideoProfileNotSupportedError(result)) { + if (IsVideoUnsupportedResult(result)) { // Not supported by hardware/driver - return VK_ERROR_INCOMPATIBLE_DRIVER std::cerr << "*** Video encode capabilities not supported by hardware/driver (" << string_VkResult_Extended(result) << ") ***" << std::endl; diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp index 7ec4d051..88c0ea54 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp @@ -404,7 +404,7 @@ VkResult EncoderConfigH264::InitDeviceCapabilities(const VulkanDeviceContext* vk intraRefreshCapabilities); if (result != VK_SUCCESS) { // Distinguish between "not supported" and "actual error" - if (IsVideoProfileNotSupportedError(result)) { + if (IsVideoUnsupportedResult(result)) { // Not supported by hardware/driver - return VK_ERROR_INCOMPATIBLE_DRIVER std::cerr << "*** Video encode capabilities not supported by hardware/driver (" << string_VkResult_Extended(result) << ") ***" << std::endl; diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp index decd6e7a..7dc76b02 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp @@ -125,7 +125,7 @@ VkResult EncoderConfigH265::InitDeviceCapabilities(const VulkanDeviceContext* vk intraRefreshCapabilities); if (result != VK_SUCCESS) { // Distinguish between "not supported" and "actual error" - if (IsVideoProfileNotSupportedError(result)) { + if (IsVideoUnsupportedResult(result)) { // Not supported by hardware/driver - return VK_ERROR_INCOMPATIBLE_DRIVER std::cerr << "*** Video encode capabilities not supported by hardware/driver (" << string_VkResult_Extended(result) << ") ***" << std::endl; diff --git a/vk_video_encoder/test/vulkan-video-enc/Main.cpp b/vk_video_encoder/test/vulkan-video-enc/Main.cpp index 66793c1a..58c5cb49 100644 --- a/vk_video_encoder/test/vulkan-video-enc/Main.cpp +++ b/vk_video_encoder/test/vulkan-video-enc/Main.cpp @@ -27,6 +27,9 @@ int main(int argc, char** argv) if (result != VK_SUCCESS) { std::cerr << "Error creating the encoder instance: " << result << std::endl; + if (IsVideoUnsupportedResult(result)) { + return VVS_EXIT_UNSUPPORTED; + } return EXIT_FAILURE; }