Files
nvsci_samples/event_sample_app/main.c
svcmobrel-release 5d1819bced Updating prebuilts and/or headers
8193be73ce0a488f62034cb87083cdf09f52cd5d - event_sample_app/block_pool.c
a0bd135d707994a41ed3a4234b5f875a268fed4d - event_sample_app/Makefile
44f6de348f8bdd5cb584b3e8cc4b05e9482dddd2 - event_sample_app/event_loop.h
6ff0f1c2d7ef2e2fa9ece6fdc850b58b87207526 - event_sample_app/block_returnsync.c
1158201e78094e9e866fa99095c9ffc2ec9f5a27 - event_sample_app/block_limiter.c
f5e2aea98ba9264ee1068a700222dff8d5d5c7a4 - event_sample_app/block_c2c.c
ef057870dade9af70656b37340e9bcad35d49380 - event_sample_app/block_multicast.c
641e3634da873970b574b23a1024b2e7155b88ff - event_sample_app/block_consumer_uc1.c
ced622a41d1a48dcb23e6a1a02ae9640ef9b837c - event_sample_app/util.h
3a1013021a572887303fb6db245b5b01fe07e9a0 - event_sample_app/block_producer_uc1.c
dac99c442185b020fbdae07bfc1e7df78343eb83 - event_sample_app/block_info.h
b5dd68bec3ae6f9049aad1cb5a86c3db4af02e17 - event_sample_app/block_presentsync.c
e0861e9fe5d160d47d758464146d7192f9c70a5f - event_sample_app/util.c
d7e42e2b6088ff4596abc7256eb018d757a4021e - event_sample_app/usecase1.h
65ffe5af6ae6bc0418f348167c473849d4697e47 - event_sample_app/block_ipc.c
b52e34443ac441a9df48029de944aa0a50d1b101 - event_sample_app/event_loop_service.c
5001f036389a4f7952cb4974dd3323908208ca30 - event_sample_app/event_loop_threads.c
a71ed037f9d77d0944f40f54cf25db8180d007e2 - event_sample_app/block_queue.c
d6bbd17599543f1760d87851150a12a2a842a24d - event_sample_app/block_common.c
40f949c4c37ab4aa4a84182b345f3de6fceab39b - event_sample_app/main.c
458833ab233a725c067bf9b1fc60ef39872eee80 - rawstream/Makefile
1fbb82e2281bb2e168c87fd20903bbed898ca160 - rawstream/rawstream_cuda.c
e26c09f1ad1a3a7d2c29dae1b38d3fd90c23af6e - rawstream/rawstream_consumer.c
2bed038ca070aa5dccd6b672a98f093340e829bb - rawstream/rawstream_producer.c
3df4e5c00a3dc002ee9877e282bd28ffa87fa6f0 - rawstream/rawstream.h
d5ffeef3c7ad2af6f6f31385db7917b5ef9a7438 - rawstream/rawstream_ipc_linux.c
f28c1cd5fe26b6dc5930d5556b54364c9b91767c - rawstream/rawstream_main.c

Change-Id: I938f32e9d8445019e4585b3076bc6cac860f8a0f
2024-09-27 12:49:12 -07:00

712 lines
23 KiB
C

/* NvSciStream Event Loop Driven Sample App - main application
*
* Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
* property and proprietary rights in and to this material, related
* documentation and any modifications thereto. Any use, reproduction,
* disclosure or distribution of this material and related documentation
* without an express license agreement from NVIDIA CORPORATION or
* its affiliates is strictly prohibited.
*/
/*
* Application info:
* This application creates a single stream with up to 4 consumers.
* Depending on command line options, the consumers can run in the same
* process as the producer, separate processes, or some combination
* thereof.
* Each consumer can be separately set to use a FIFO or mailbox queue,
* and an optional limiter block can be requested for each one.
*
* An application can check for NvSciStream events either by polling
* or waiting for a single block at a time through a NvSciStream function,
* or by binding an NvSciEventService to the blocks, allowing it to
* wait for events on multiple NvSciStream blocks (as well as other
* components capable of signalling NvSciEvents) simultaneously.
* If an application carefully controls the order of operations, it
* may be able to wait for specific events in a single thread. But
* more generally, NvSciStream supports an event loop driven model,
* where any event may occur and the application responds appropriately.
* This can be done either with a single thread handling all blocks,
* or separate threads for each block. This application provides
* examples of both use cases.
*
* For testing and demonstration purposes, the default target setup for
* the NVIDIA SDK includes indexed NvSciIpc channels with base name
* "nvscistream_", which are connected together in even/odd pairs.
* (So nvscistream_0 is connected to nvscistream_1, nvscistream_2 is
* connected to nvscistream_3, and so on.) This sample application is
* hard-coded to use these channels when streaming between processes.
* A production application should be modified to use the channels
* defined for the production target.
*
* This application is intended to illustrate how to do full setup of a
* stream, assuming everything is working correctly. It does all
* necessary error checking to confirm setup succeeded, but does not
* attempt any recovery or do a full teardown in the event a failure
* is detected.
*
* Our approach to abstracting the event and per-block support is object
* oriented and would lend itself well to C++. But for simplicity,
* since NvSciStream itself is a C interface, we have restricted this
* sample application to C code.
*
* Unless otherwise stated, all functions in all files return 1 to
* indicate success and 0 to indicate failure.
*/
#include <unistd.h>
#include <stdio.h>
#include "nvscisync.h"
#include "nvscibuf.h"
#include "nvsciipc.h"
#include "nvscistream.h"
#include "event_loop.h"
#include "block_info.h"
/* Base name for all IPC channels */
static const char ipcBaseName[] = "nvscistream_";
/* Event handling function table */
EventFuncs const* eventFuncs = NULL;
/* Top level use-case setup function pointers */
int32_t (*createProducer)(
NvSciStreamBlock* producer,
NvSciStreamBlock pool) = createProducer_Usecase1;
int32_t (*createConsumer)(
NvSciStreamBlock* consumer,
NvSciStreamBlock queue,
uint32_t index) = createConsumer_Usecase1;
/* NvSci modules */
NvSciSyncModule sciSyncModule;
NvSciBufModule sciBufModule;
/* NvSciIpc Endpoint */
NvSciIpcEndpoint ipcEndpoint = 0U;
/* Common options for all blocks */
CommonOptions opts;
/* Options for producer */
typedef struct {
uint32_t resident;
uint32_t numConsumer;
uint32_t numPacket;
} ProducerOptions;
/* Options for each consumer */
typedef struct {
uint32_t resident;
uint32_t useMailbox;
uint32_t useLimiter;
uint32_t c2cMode;
uint32_t c2cSrcUseMailbox;
uint32_t c2cDstNumPacket;
char srcChannel[32];
char dstChannel[32];
} ConsumerOptions;
/* Print command line options */
static void print_usage(const char *str)
{
printf("%s [options]\n", str);
printf(" For single- or inter-process/chip operation:\n");
printf(" -m <count> [default 1, max %d]\n", MAX_CONSUMERS);
printf(" number of multicast consumers\n");
printf(" (ignored if process doesn't own producer\n");
printf(" -f <count> [default 3]\n");
printf(" number of packets in main pool\n");
printf(" (ignored if process doesn't own producer\n");
printf(" -l <index> <limit> [default - not used]\n");
printf(" use limiter block for indexed consumer\n");
printf(" (ignored if process doesn't own producer\n");
printf(" -q <index> {f|m} [default f]\n");
printf(" use fifo (f) or maibox (m) for indexed consumer\n");
printf(" (ignored if process doesn't own the indexed consumer\n");
printf(" -e {s|t} [default s]\n");
printf(" s : events are handled through a single service\n");
printf(" t : events are handled with separate per-block threads\n");
printf(" -s {y|r} [default r]\n");
printf(" y : NvSciColor_Y8U8Y8V8 Image Color Format in use case 2\n");
printf(" r : NvSciColor_A8R8G8B8 Image Color Format in use case 2\n");
printf(" -u <index> [default 1]\n");
printf(" use case (must be same for all processes)\n");
printf(" 1 : CUDA (rt) producer to CUDA (rt) consumer\n");
#if (NV_SUPPORT_NVMEDIA == 1)
printf(" 2 : NvMedia producer to CUDA (rt) consumer\n");
#endif
printf(" -i [default - not used]\n");
printf(" set endpoint info and query info from other endpoints\n");
printf(" For inter-process operation:\n");
printf(" -p\n");
printf(" producer resides in this process\n");
printf(" -c <index> \n");
printf(" indexed consumer resides in this process\n");
printf(" For inter-chip (C2C) operation:\n");
printf(" -P <index> <Ipc endpoint name>\n");
printf(" producer resides in this process\n");
printf(" Ipc endpoint used by the producer to communicate with the "
"indexed chip-to-chip (C2C) consumer\n");
printf(" -C <index> <Ipc endpoint name>\n");
printf(" indexed consumer resides in this process\n");
printf(" Ipc endpoint used by this chip-to-chip (C2C) consumer\n");
printf(" -C and -c can't be used simultaneously.\n");
printf(" (ignored if process owns producer)\n");
printf(" -F <index> <count> [default 3]\n");
printf(" number of packets in pool attached to the IpcDst block "
"of the indexed C2C consumer\n");
printf(" set along with the indexed C2C consumer.\n");
printf(" (ignored if process doesn't own indexed C2C consumer)\n");
printf(" -Q <index> {f|m} [default f]\n");
printf(" use fifo (f) or maibox (m) for C2C IpcSrc of indexed "
"consumer.\n");
printf(" Can't specify same index as -c)\n");
printf(" set in the producer process.\n");
printf(" (ignored if process doesn't own producer)\n");
}
/* Set up chain of producer side blocks, up to optional multicast */
static int32_t setupProducerChain(
NvSciStreamBlock* producerLink,
ProducerOptions* prodOpts)
{
/* Create pool */
NvSciStreamBlock poolBlock;
if (!createPool(&poolBlock, prodOpts->numPacket, false)) {
return 0;
}
/* Create producer */
NvSciStreamBlock producerBlock;
if (!createProducer(&producerBlock, poolBlock)) {
return 0;
}
/* If multicast required, add the block. */
if (prodOpts->numConsumer > 1) {
/* Create multicast block */
NvSciStreamBlock multicastBlock;
if (!createMulticast(&multicastBlock, prodOpts->numConsumer)) {
return 0;
}
/* Connect to producer */
if (NvSciError_Success !=
NvSciStreamBlockConnect(producerBlock, multicastBlock)) {
printf("Failed to connect multicast to producer\n");
return 0;
}
/* Multicast block is end of chain */
*producerLink = multicastBlock;
} else {
/* Producer block is end of chain */
*producerLink = producerBlock;
}
return 1;
}
/* Set up chain of consumer side blocks */
static int32_t setupConsumerChain(
NvSciStreamBlock* consumerLink,
ConsumerOptions* consOpts,
uint32_t index)
{
/*
* Note: Currently the consumer "chain" just consists of the consumer
* itself and its associated queue. We follow this paradigm to
* allow easy addition of new optional blocks in the future.
*/
/* Create queue */
NvSciStreamBlock queueBlock;
if (!createQueue(&queueBlock, consOpts->useMailbox)) {
return 0;
}
/* Create consumer */
NvSciStreamBlock consumerBlock;
if (!createConsumer(&consumerBlock, queueBlock, index)) {
return 0;
}
/* Consumer block is start of chain */
*consumerLink = consumerBlock;
return 1;
}
/* Add additional branch options */
static int32_t setupBranchOptions(
NvSciStreamBlock* consumerLink,
ConsumerOptions* consOpts)
{
/* If limiter requested, add it */
if (consOpts->useLimiter) {
/* If a consumer may generate unreliable fences, a ReturnSync block can
* be added as the downstream of the Limiter block for that consumer,
* to isolate any packets with bad fences.
*/
NvSciStreamBlock returnSyncBlock;
if (!createReturnSync(&returnSyncBlock)) {
return 0;
}
/* Connect to incoming consumer chain */
if (NvSciError_Success !=
NvSciStreamBlockConnect(returnSyncBlock, *consumerLink)) {
printf("Failed to connect returnSyncBlock to consumer chain\n");
return 0;
}
/* ReturnSync is new end of chain */
*consumerLink = returnSyncBlock;
/* Create limiter */
NvSciStreamBlock limiterBlock;
if (!createLimiter(&limiterBlock, consOpts->useLimiter)) {
return 0;
}
/* Connect to incoming consumer chain */
if (NvSciError_Success !=
NvSciStreamBlockConnect(limiterBlock, *consumerLink)) {
printf("Failed to connect limiter to consumer chain\n");
return 0;
}
/* Limiter is new end of chain */
*consumerLink = limiterBlock;
}
return 1;
}
/* Set up IPC from producer to consumer */
static int32_t setupProdToConsIPC(
NvSciStreamBlock* consumerLink,
ConsumerOptions* consOpts)
{
if (!consOpts->c2cMode) {
/* Create IPC block */
if (!createIpcSrc(consumerLink, consOpts->srcChannel)) {
return 0;
}
} else {
/* Create a queue for C2C src block */
NvSciStreamBlock queueBlock;
if (!createQueue(&queueBlock, consOpts->c2cSrcUseMailbox)) {
return 0;
}
/* Create C2C block */
if (!createC2cSrc(consumerLink, consOpts->srcChannel, queueBlock)) {
return 0;
}
/* If mailbox is used with C2CSrc, then create presentSync block */
if (1U == consOpts->c2cSrcUseMailbox) {
NvSciStreamBlock presentSyncBlock;
if (!createPresentSync(&presentSyncBlock)) {
return 0;
}
if (NvSciError_Success !=
NvSciStreamBlockConnect(presentSyncBlock, *consumerLink)) {
printf("Failed to connect PresentSync to consumer chain\n");
return 0;
}
/* PresentSync is new end of chain */
*consumerLink = presentSyncBlock;
}
}
return 1;
}
/* Set up IPC from consumer to producer */
static int32_t setupConsToProdIPC(
NvSciStreamBlock* producerLink,
ConsumerOptions* consOpts)
{
if (!consOpts->c2cMode) {
/* Create IPC block */
if (!createIpcDst(producerLink, consOpts->dstChannel)) {
return 0;
}
} else {
/* Create a pool for C2C dst block */
NvSciStreamBlock poolBlock;
if (!createPool(&poolBlock, consOpts->c2cDstNumPacket, true)) {
return 0;
}
/* Create C2C block */
if (!createC2cDst(producerLink, consOpts->dstChannel, poolBlock)) {
return 0;
}
}
return 1;
}
/*
* Main application function.
* As per standards, return of 0 indicates success and anything
* else is failure.
*/
int main(int argc, char *argv[])
{
uint32_t i;
int ret = 0;
/* Initialize parameters */
uint32_t badParam = 0U;
uint32_t multiProcess = 0U;
uint32_t multiSOC = 0U;
uint32_t eventOption = 0U;
ProducerOptions prodOpts = {.resident=0U, .numConsumer=1U, .numPacket=3U};
ConsumerOptions consOpts[MAX_CONSUMERS];
memset(consOpts, 0, sizeof(consOpts));
memset(&opts, 0, sizeof(CommonOptions));
/* Parse command line */
int32_t opt;
while ((opt = getopt(argc, argv, "m:f:l:q:e:s:u:ipc:P:C:F:Q:")) != EOF) {
switch (opt) {
case 'm': /* set number of consumers */
prodOpts.numConsumer = atoi(optarg);
if ((prodOpts.numConsumer < 1U) ||
(prodOpts.numConsumer > MAX_CONSUMERS)) {
badParam = 1U;
}
break;
case 'f': /* set number of packets */
prodOpts.numPacket = atoi(optarg);
if ((prodOpts.numPacket < 1U) ||
(prodOpts.numPacket > MAX_PACKETS)) {
badParam = 1U;
}
break;
case 'l': /* use limiter block for indexed consumer */
i = atoi(optarg);
if (i >= MAX_CONSUMERS) {
badParam = 1U;
} else {
consOpts[i].useLimiter = atoi(argv[optind++]);
}
break;
case 'q': /* use specified queue for indexed consumer */
i = atoi(optarg);
if (i >= MAX_CONSUMERS) {
badParam = 1U;
} else {
char t = argv[optind++][0];
if (t == 'm') {
consOpts[i].useMailbox = 1U;
} else if (t == 'f') {
consOpts[i].useMailbox = 0U;
} else {
badParam = 1U;
}
}
break;
case 'e': /* set event handling mechanism */
if (optarg[0] == 's') {
eventOption = 0U;
} else if (optarg[0] == 't') {
eventOption = 1U;
} else {
badParam = 1U;
}
break;
case 's': /* set Image Color Format type */
if (optarg[0] == 'r') {
opts.yuv = false;
} else if (optarg[0] == 'y') {
opts.yuv = true;
} else {
badParam = 1U;
}
break;
case 'u': /* set use case */
i = atoi(optarg);
if (i == 1) {
createProducer = createProducer_Usecase1;
createConsumer = createConsumer_Usecase1;
}
#if (NV_SUPPORT_NVMEDIA == 1)
else if (i == 2) {
createProducer = createProducer_Usecase2;
createConsumer = createConsumer_Usecase2;
}
#endif
else {
badParam = 1U;
}
break;
case 'i':
opts.endInfo = true;
break;
/* For inter - process operation */
case 'p': /* set producer resident */
prodOpts.resident = 1U;
multiProcess = 1U;
break;
case 'c': /* set consumer resident */
i = atoi(optarg);
if (i >= MAX_CONSUMERS) {
badParam = 1U;
} else {
consOpts[i].resident = 1U;
multiProcess = 1U;
}
break;
/* For inter - chip (C2C) operation */
case 'P': /* set ipc endpoint for C2C */
i = atoi(optarg);
if (i >= MAX_CONSUMERS) {
badParam = 1U;
} else {
/* Ipc channel used to communicate with this C2C consumer */
strcpy(consOpts[i].srcChannel, argv[optind++]);
consOpts[i].c2cMode = 1U;
prodOpts.resident = 1U;
multiProcess = 1U;
multiSOC = 1U;
}
break;
case 'C': /* set C2C mode */
i = atoi(optarg);
if (i >= MAX_CONSUMERS) {
badParam = 1U;
} else {
/* Ipc channel name used by this C2C consumer */
strcpy(consOpts[i].dstChannel, argv[optind++]);
consOpts[i].c2cMode = 1U;
multiProcess = 1U;
multiSOC = 1U;
if (consOpts[i].c2cDstNumPacket == 0U) {
/* default packet size 3 if not set already */
consOpts[i].c2cDstNumPacket = 3U;
}
}
break;
case 'F': /* set number of packets for C2C Dst of indexed consumer */
i = atoi(optarg);
if (i >= MAX_CONSUMERS) {
badParam = 1U;
} else {
consOpts[i].c2cDstNumPacket = atoi(argv[optind++]);
if ((consOpts[i].c2cDstNumPacket < 1U) ||
(consOpts[i].c2cDstNumPacket > MAX_PACKETS)) {
badParam = 1U;
}
}
break;
case 'Q': /* use specified queue for C2C Src of indexed consumer */
i = atoi(optarg);
if (i >= MAX_CONSUMERS) {
badParam = 1U;
} else {
char t = argv[optind++][0];
if (t == 'm') {
consOpts[i].c2cSrcUseMailbox = 1U;
} else if (t == 'f') {
consOpts[i].c2cSrcUseMailbox = 0U;
} else {
badParam = 1U;
}
}
break;
default:
badParam = 1U;
break;
}
}
/* Handle parsing failure */
if (badParam) {
print_usage(argv[0]);
return 1;
}
/* Check validity of the combination C2C & non-C2C consumers */
for (i=0U; i<MAX_CONSUMERS; ++i) {
if (prodOpts.resident) {
/* C2C consumer cannot be in the same process as producer */
if (consOpts[i].resident && consOpts[i].c2cMode) {
return 1;
}
} else {
/* There is C2C consumer in this process,
* can't have non-C2C ones
*/
if (multiSOC && consOpts[i].resident) {
return 1;
}
/* Now make consumer resident if C2C */
if (consOpts[i].c2cMode) {
consOpts[i].resident = 1U;
}
}
}
/* Fill in other options based on those specified */
if (!multiProcess) {
/* If no multi-process option specified, everything is resident */
prodOpts.resident = 1U;
for (i=0U; i<prodOpts.numConsumer; ++i) {
consOpts[i].resident = 1U;
}
} else {
/* If not in producer process, will just loop over full list */
if (!prodOpts.resident) {
prodOpts.numConsumer = MAX_CONSUMERS;
}
/* Channel names are derived from base and index */
for (i=0U; i<prodOpts.numConsumer; ++i) {
if (!consOpts[i].c2cMode) {
sprintf(consOpts[i].srcChannel, "%s%d", ipcBaseName, 2*i+0);
sprintf(consOpts[i].dstChannel, "%s%d", ipcBaseName, 2*i+1);
}
}
}
/* Select and initialize event-handling based on chosen method */
eventFuncs = eventOption ? &eventFuncs_Threads : &eventFuncs_Service;
if (!eventFuncs->init() != 0) {
return 1;
}
/*
* Initialize NvSci libraries
*/
if (NvSciError_Success != NvSciSyncModuleOpen(&sciSyncModule)) {
printf("Unable to open NvSciSync module\n");
}
if (NvSciError_Success != NvSciBufModuleOpen(&sciBufModule)) {
printf("Unable to open NvSciBuf module\n");
}
if (NvSciError_Success != NvSciIpcInit()) {
printf("Unable to initialize NvSciIpc\n");
}
/*
* If producer is resident, create producer block chain and attach
* all consumers.
*/
if (prodOpts.resident) {
/* Set up producer chain (up through any multicast block) */
NvSciStreamBlock producerLink;
if (!setupProducerChain(&producerLink, &prodOpts)) {
return 1;
}
/*
* For each consumer, either set up the consumer chain or create
* the IPC block to communicate with it, depending on whether the
* consumer is resident.
*/
for (i=0U; i<prodOpts.numConsumer; ++i) {
/* Create consumer or IPC to consumer */
NvSciStreamBlock consumerLink;
if (consOpts[i].resident) {
if (!setupConsumerChain(&consumerLink, &consOpts[i], i)) {
return 1;
}
} else {
if (!setupProdToConsIPC(&consumerLink, &consOpts[i])) {
return 1;
}
}
/* Add any other options (e.g. limiter) for this branch */
if (!setupBranchOptions(&consumerLink, &consOpts[i])) {
return 1;
}
/* Attach to producer chain */
if (NvSciError_Success !=
NvSciStreamBlockConnect(producerLink, consumerLink)) {
printf("Failed to connect consumer %d to producer\n", i);
return 1;
}
}
}
/*
* Otherwise, create any consumer chains resident in this process,
* and connect with IPC back to the producer process.
*/
else {
for (i=0U; i<prodOpts.numConsumer; ++i) {
if (consOpts[i].resident) {
/* Create consumer */
NvSciStreamBlock consumerLink;
if (!setupConsumerChain(&consumerLink, &consOpts[i], i)) {
return 1;
}
/* Create IPC block */
NvSciStreamBlock producerLink;
if (!setupConsToProdIPC(&producerLink, &consOpts[i])) {
return 1;
}
/* Connect blocks */
if (NvSciError_Success !=
NvSciStreamBlockConnect(producerLink, consumerLink)) {
printf("Failed to connect consumer %d to producer\n", i);
return 1;
}
}
}
}
/* Enter event loop(s) until all blocks are done */
if (!eventFuncs->loop()) {
ret = 1;
}
if (sciBufModule != NULL) {
NvSciBufModuleClose(sciBufModule);
sciBufModule = NULL;
}
if (sciSyncModule != NULL) {
NvSciSyncModuleClose(sciSyncModule);
sciSyncModule = NULL;
}
/* Close the NvSciIpc endpoint */
if (ipcEndpoint) {
if (NvSciError_Success !=
NvSciIpcCloseEndpointSafe(ipcEndpoint, false)) {
printf("Failed to close ipc endpoint\n");
}
ipcEndpoint = 0U;
}
NvSciIpcDeinit();
return ret;
}