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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions