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.

how to avoid race condition when using PySurfaceConverter in multiple processes? #554

@niujiabenbeng

Description

@niujiabenbeng

Race conditions occur when using PySurfaceConverter in multiple processes. #506 suggests cloning the output surface, which reduces the problem a lot but does not solve it.

As the following code shows, we convert the same surface twice, but the results are not equal when using multiple processing.

#! /usr/bin/env python
# coding: utf-8

# pylint: disable=all

import multiprocessing
import numpy as np
import PyNvCodec as nvc
import PytorchNvCodec as pnvc


class NvColorConverter:
    "Color converter using PySurfaceConverter."

    def __init__(self, width, height, gpuid=0):
        # yapf: disable
        self.width, self.height = width, height
        self.context = nvc.ColorspaceConversionContext(
            nvc.ColorSpace.BT_601, nvc.ColorRange.MPEG)
        self.to_yuv = nvc.PySurfaceConverter(
            width, height, nvc.PixelFormat.NV12,
            nvc.PixelFormat.YUV420, gpuid)
        self.to_rgb = nvc.PySurfaceConverter(
            width, height, nvc.PixelFormat.YUV420,
            nvc.PixelFormat.RGB, gpuid)
        self.to_planar = nvc.PySurfaceConverter(
            width, height, nvc.PixelFormat.RGB,
            nvc.PixelFormat.RGB_PLANAR, gpuid)
        self.downloader = nvc.PySurfaceDownloader(
            width, height, nvc.PixelFormat.RGB_PLANAR, gpuid)
        # yapf: enable

    def convert_color(self, surface):
        surface = self.to_yuv.Execute(surface, self.context)
        surface = self.to_rgb.Execute(surface, self.context)
        surface = self.to_planar.Execute(surface, self.context)
        # We clone the surface as suggested.
        surface = surface.Clone()
        frame = np.ndarray(shape=(0, ), dtype=np.uint8)
        self.downloader.DownloadSingleSurface(surface, frame)
        return frame


# arg is a placeholder
def test_decode_video(arg):
    path, gpuid = "./test.mp4", 0
    dec = nvc.PyNvDecoder(path, gpuid)
    converter = NvColorConverter(dec.Width(), dec.Height(), gpuid)
    for i in range(dec.Numframes()):
        surface = dec.DecodeSingleSurface()
        if surface.Empty(): break
        # We use the same converter to convert the same surface twice,
        # When processes = 1, both arrays have the same value,
        # When processes > 1, two arrays are not equal.
        array1 = converter.convert_color(surface)
        array2 = converter.convert_color(surface)
        if not np.array_equal(array1, array2):
            print("frame not match")


def main():
    with multiprocessing.Pool(processes=8) as pool:
        pool.map(test_decode_video, [None] * 16)


if __name__ == "__main__":
    main()

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