def _free(self): """Free any resources that have been allocated by the object. This must be called by the stream after all frames have been processed. """ if self._numpySwsCtx is not None: swscale.sws_freeContext(self._numpySwsCtx) self._numpySwsCtx = None if self._pilSwsCtx is not None: swscale.sws_freeContext(self._pilSwsCtx) self._pilSwsCtx = None if self._picture is not None: avcodec.avpicture_free(self._picture) self._picture = None
def _initNumpyProcessing(self, pixelFormat, pixelAccess, colorAccess): """Initialize decoding into a numpy array. This has to be called at least once before a frame is converted into a numpy array. If the layout of the numpy array changes, the method has to be called again. The method creates the numpy array, the swscale context to convert the image data and the _lineSizes and _dataPtrs arrays for the sws_scale() call. """ # Check the pixelAccess value... if pixelAccess not in [WIDTH_HEIGHT, HEIGHT_WIDTH]: raise ValueError("Invalid pixelAcess value") # Check the colorAccess value... if colorAccess not in [SEPARATE_CHANNELS, COMBINED_CHANNELS]: raise ValueError("Invalid colorAccess value") # Check the pixel format... numChannels = None dstPixFmt = None if pixelFormat==RGB: numChannels = 3 dstPixFmt = decls.PIX_FMT_RGB24 elif pixelFormat==BGR: numChannels = 3 dstPixFmt = decls.PIX_FMT_BGR24 elif pixelFormat==RGBA: numChannels = 4 dstPixFmt = decls.PIX_FMT_RGBA elif pixelFormat==ARGB: numChannels = 4 dstPixFmt = decls.PIX_FMT_ARGB elif pixelFormat==ABGR: numChannels = 4 dstPixFmt = decls.PIX_FMT_ABGR elif pixelFormat==BGRA: numChannels = 4 dstPixFmt = decls.PIX_FMT_BGRA elif pixelFormat==GRAY: numChannels = 1 dstPixFmt = decls.PIX_FMT_GRAY8 else: raise ValueError("Invalid pixelFormat value") width,height = self.size # Allocate the numpy array that will hold the converted image. # The memory layout of the buffer is always so that the image is stored # in rows and all the channels are stored together with a pixel. # Example: Row 0: RGB-RGB-RGB-RGB... # Row 1: RGB-RGB-RGB-RGB... # ... # To get from one channel to the next channel, you always have to add 1 byte # (for int8 channels). To get to the next x position, you have to add # numChannels bytes and to get to the next y position you have to add # width*numChannels bytes. # The pixelAccess and colorAccess parameters don't affect this memory # layout, they only affect how a pixel is accessed via numpy. if colorAccess==SEPARATE_CHANNELS: # Allocate a RGB buffer... if pixelAccess==WIDTH_HEIGHT: self._numpyArray = numpy.empty((width,height,numChannels), dtype=numpy.uint8) # Adjust the strides so that image rows are consecutive self._numpyArray.strides = (numChannels, numChannels*width, 1) else: self._numpyArray = numpy.empty((height,width,numChannels), dtype=numpy.uint8) else: if numChannels not in [1,4]: raise ValueError("COMBINED_CHANNELS pixel access can only be used with 1-channel or 4-channel pixel formats") if numChannels==1: dtype = numpy.uint8 else: dtype = numpy.uint32 # Allocate a RGBA buffer (where a RGBA value is stored as a uint32) if pixelAccess==WIDTH_HEIGHT: self._numpyArray = numpy.empty((width,height), dtype=dtype, order="F") else: self._numpyArray = numpy.empty((height,width), dtype=dtype) # Free any previously allocated context if self._numpySwsCtx is not None: swscale.sws_freeContext(self._numpySwsCtx) self._numpySwsCtx = None # Allocate the swscale context self._numpySwsCtx = swscale.sws_getContext(width, height, self._srcPixFmt, width, height, dstPixFmt, 1) # Initialize the lineSizes and dataPtrs array that are passed into sws_scale(). # The first data pointer points to the beginning of the numpy buffer. DataPtrType = ctypes.POINTER(ctypes.c_uint8) self._lineSizes = (4*ctypes.c_int)(numChannels*width,0,0,0) self._dataPtrs = (4*DataPtrType)(self._numpyArray.ctypes.data_as(DataPtrType), None, None, None) # Store the parameters so that we can detect parameter changes self._numpyBufferParams = (pixelFormat, pixelAccess, colorAccess)