from gst_generator import createPipelineIterator from util import optional_chain import scrypted_sdk from typing import Any from urllib.parse import urlparse import vipsimage import pilimage import platform Gst = None try: import gi gi.require_version('Gst', '1.0') gi.require_version('GstBase', '1.0') from gi.repository import Gst except: pass async def generateVideoFramesGstreamer(mediaObject: scrypted_sdk.MediaObject, options: scrypted_sdk.VideoFrameGeneratorOptions = None, filter: Any = None, h264Decoder: str = None) -> scrypted_sdk.VideoFrame: ffmpegInput: scrypted_sdk.FFmpegInput = await scrypted_sdk.mediaManager.convertMediaObjectToJSON(mediaObject, scrypted_sdk.ScryptedMimeTypes.FFmpegInput.value) container = ffmpegInput.get('container', None) videosrc = ffmpegInput.get('url') videoCodec = optional_chain(ffmpegInput, 'mediaStreamOptions', 'video', 'codec') if videosrc.startswith('tcp://'): parsed_url = urlparse(videosrc) videosrc = 'tcpclientsrc port=%s host=%s' % ( parsed_url.port, parsed_url.hostname) if container == 'mpegts': videosrc += ' ! tsdemux' elif container == 'sdp': videosrc += ' ! sdpdemux' else: raise Exception('unknown container %s' % container) elif videosrc.startswith('rtsp'): videosrc = 'rtspsrc buffer-mode=0 location=%s protocols=tcp latency=0 is-live=false' % videosrc if videoCodec == 'h264': videosrc += ' ! rtph264depay ! h264parse' videocaps = 'video/x-raw' # if options and options.get('resize'): # videocaps = 'videoscale ! video/x-raw,width={width},height={height}'.format(width=options['resize']['width'], height=options['resize']['height']) format = options and options.get('format') # I420 is a cheap way to get gray out of an h264 stream without color conversion. if format == 'gray': format = 'I420' bands = 1 else: format = 'RGB' bands = 3 videocaps += ',format={format}'.format(format=format) decoder = None def setDecoderClearDefault(value: str): nonlocal decoder decoder = value if decoder == 'Default': decoder = None setDecoderClearDefault(None) if videoCodec == 'h264': setDecoderClearDefault(h264Decoder) if not decoder: # hw acceleration is "safe" to use on mac, but not # on other hosts where it may crash. # defaults must be safe. if platform.system() == 'Darwin': decoder = 'vtdec_hw' else: decoder = 'avdec_h264' else: # decodebin may pick a hardware accelerated decoder, which isn't ideal # so use a known software decoder for h264 and decodebin for anything else. decoder = 'decodebin' videosrc += ' ! {decoder} ! queue leaky=downstream max-size-buffers=0 ! videoconvert ! {videocaps}'.format(decoder=decoder, videocaps=videocaps) gst, gen = createPipelineIterator(videosrc) async for gstsample in gen(): caps = gstsample.get_caps() height = caps.get_structure(0).get_value('height') width = caps.get_structure(0).get_value('width') gst_buffer = gstsample.get_buffer() result, info = gst_buffer.map(Gst.MapFlags.READ) if not result: continue try: if vipsimage.pyvips: vips = vipsimage.new_from_memory(info.data, width, height, bands) vipsImage = vipsimage.VipsImage(vips) try: mo = await vipsimage.createVipsMediaObject(vipsImage) yield mo finally: vipsImage.vipsImage = None vips.invalidate() else: pil = pilimage.new_from_memory(info.data, width, height, bands) pilImage = pilimage.PILImage(pil) try: mo = await pilimage.createPILMediaObject(pilImage) yield mo finally: pilImage.pilImage = None pil.close() finally: gst_buffer.unmap(info)