Skip to content
This repository was archived by the owner on Jun 10, 2024. It is now read-only.
This repository was archived by the owner on Jun 10, 2024. It is now read-only.

Continuous growth of CPU and memory #511

Open

Description

@liujiachang

When I used SampleDecodeMultiThread. py for testing, the CPU usage and memory usage of the program continued to increase. Initially, it only occupied 300% CPU and 1.8G mem. After running for about ten minutes, it occupied 1000% CPU and 2.3G mem. I slightly modified the test script and read fifty video streams at the same time. I don't think it should consume so much resources.
Here is my code:

import PyNvCodec as nvc
# import numpy as np
import argparse
from threading import Thread
import threading
# import cv2
import time


class Worker(Thread):
    def __init__(self, gpuID, encFile):
        Thread.__init__(self)
        self.st = time.time()
        self.pt = time.time()
        # Create Decoder with given CUDA context & stream.
        self.nvDec = nvc.PyNvDecoder(encFile, gpuID)

        self.width, self.height = self.nvDec.Width(), self.nvDec.Height()

        # Determine colorspace conversion parameters.
        # Some video streams don't specify these parameters so default values
        # are most widespread bt601 and mpeg.
        cspace, crange = self.nvDec.ColorSpace(), self.nvDec.ColorRange()
        if nvc.ColorSpace.UNSPEC == cspace:
            cspace = nvc.ColorSpace.BT_601
        if nvc.ColorRange.UDEF == crange:
            crange = nvc.ColorRange.MPEG
        self.cc_ctx = nvc.ColorspaceConversionContext(cspace, crange)
        # print("Color space: ", str(cspace))
        # print("Color range: ", str(crange))

        # Initialize colorspace conversion chain
        if self.nvDec.ColorSpace() != nvc.ColorSpace.BT_709:
            self.nvYuv = nvc.PySurfaceConverter(
                self.width,
                self.height,
                self.nvDec.Format(),
                nvc.PixelFormat.YUV420,
                0
            )
        else:
            self.nvYuv = None

        if self.nvYuv:
            self.nvCvt = nvc.PySurfaceConverter(
                self.width,
                self.height,
                self.nvYuv.Format(),
                nvc.PixelFormat.BGR,
                0
            )
        else:
            self.nvCvt = nvc.PySurfaceConverter(
                self.width,
                self.height,
                self.nvDec.Format(),
                nvc.PixelFormat.BGR,
                0
            )
        # # resize
        # self.nvRes = nvc.PySurfaceResizer(
        #     hwidth, hheight, self.nvCvt.Format(), 0
        # )
        # self.nvDwn = nvc.PySurfaceDownloader(
        #     hwidth, hheight, self.nvRes.Format(), 0
        # )
        self.nvDwn = nvc.PySurfaceDownloader(
            self.width, self.height, self.nvCvt.Format(), 0
        )
        self.num_frame = 0

    def run(self):
        try:
            while True:
                try:
                    self.rawSurface = self.nvDec.DecodeSingleSurface()
                    if self.rawSurface.Empty():
                        print("No more video frames")
                        break
                except nvc.HwResetException:
                    print("Continue after HW decoder was reset")
                    continue
                if 0 == self.num_frame % 5:
                    if self.nvYuv:
                        self.yuvSurface = self.nvYuv.Execute(self.rawSurface, self.cc_ctx)
                        self.cvtSurface = self.nvCvt.Execute(self.yuvSurface, self.cc_ctx)
                    else:
                        self.cvtSurface = self.nvCvt.Execute(self.rawSurface, self.cc_ctx)
                    if self.cvtSurface.Empty():
                        print("Failed to do color conversion")
                        break

                    # self.resSurface = self.nvRes.Execute(self.cvtSurface)
                    # if self.resSurface.Empty():
                    #     print("Failed to resize surface")
                    #     break

                    # self.rawFrame = np.ndarray(
                    #     shape=(self.cvtSurface.HostSize()), dtype=np.uint8
                    # )
                    # success = self.nvDwn.DownloadSingleSurface(
                    #     self.cvtSurface, self.rawFrame
                    # )
                    #
                    # if not (success):
                    #     print("Failed to download surface")
                    #     break
                    # print(self.rawFrame.shape)

                    # self.rawFrame = self.rawFrame.reshape((self.height, self.width, 3))
                    # cv2.imwrite('test.png', self.rawFrame)
                    # break

                    # print(f"Thread {self.ident} at frame {self.num_frame}")
                if time.time() - self.pt > 5:
                    self.pt = time.time()
                    print(f"Thread {threading.current_thread().name} fps {self.num_frame / (time.time() - self.st)}")
                self.num_frame += 1
        except Exception as e:
            print(getattr(e, "message", str(e)))


def create_threads(gpu_id, input_file, num_threads):

    thread_pool = []
    for i in range(0, num_threads):
        thread = Worker(gpu_id, input_file)
        thread.start()
        thread_pool.append(thread)

    for thread in thread_pool:
        thread.join()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='frame decode')
    parser.add_argument('--nums', type=int, default=50, help="stream nums")
    parser.add_argument('--gpu', type=int, default=0, help="gpu id")
    args = parser.parse_args()
    stream = '*******'  #  This is my video streaming address, sorry I cannot disclose it.

    create_threads(args.gpu, stream, args.nums)

Activity

  1. david-molnar-oculai commented on Aug 31, 2023

    @david-molnar-oculai

    We're using the decoder and encoder and run them continuously to process a stream of 4 second clips. The memory and cpu doesn't increase, it stays constant. We used https://github.com/NVIDIA/VideoProcessingFramework/blob/master/samples/SamplePyTorch.py as a base. Also, more streams are processed on the same machine, using different threads in the same process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions