blob: 1285f81e1dc58b788ca74a348bdc745b1f8c0a43 [file] [edit]
#
# GPAC - Multimedia Framework C SDK
#
# Authors: Jean Le Feuvre
# Copyright (c) Telecom Paris 2020-2026
# All rights reserved
#
# Python ctypes bindings for GPAC (core initialization and filters API only)
#
# GPAC is free software; you can redistribute it and/or modify
# it under the terfsess of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# GPAC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; see the file COPYING. If not, write to
# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
#
##
# \defgroup pyapi_grp Python APIs
# \brief Python API for libgpac.
#
# # Foreword
# This module provides [ctypes](https://docs.python.org/3/library/ctypes.html) bindings for libgpac. These bindings currently allow:
# - initializing libgpac, setting global arguments for the library
# - creation, running and destruction of a filter session in blocking mode or non-blocking mode
# - inspection of filters in a session
# - user tasks scheduling
# - Creation of custom filters in Python
#
# \note This is an initial work and more bindings might be needed, feel free to contribute/PR on https://github.com/gpac/gpac
#
# # Error handling
#
# Errors are handled through raising exceptions, except callback methods wich must return a \ref GF_Err value.
# # Properties handling
#
# Properties types are automatically converted to and from string. If the property name is not a built-in property type, the property is assumed to be a user-defined property.
# For example, when querying or setting a stream type property, use the property name `StreamType`.
# See [`gpac -h props`](https://wiki.gpac.io/Filters/filters_properties/) for the complete list of built-in property names.
#
# Properties values are automatically converted to or from python types whenever possible. Types with no python equivalent (vectors, fractions) are defined as classes in python.
# For example:
# - when setting a UIntList property, pass a python list of ints
# - when reading a UIntList property, a python list of ints will be returned
# - when setting a PropVec2i property, pass a PropVec2i object
# - when setting a PropVec2iList property, pass a python list of PropVec2i
#
# 4CCs are handled as strings in python, and list of 4CCs are handled as list of strings
#
# The following builtin property types are always handled as strings in Python instead of int in libgpac :
# - StreamType: string containing the streamtype name
# - CodecID: string containing the codec name
# - Enumeration properties (PixelFormat, AudioFormat, ...): string containing the corresponding name
#
# # Basic setup
#
# You can initialize libgpac before any other calls to set memory tracker or profile. When importing the module, libgpac is by default initialized with no memory tracking and using the default profile.
# You currently must uninintialize it after everything gpac-related is done.
# You also must destroy explicitly the filter session, as otherwise the garbage collection on the filter session object could happen after libgpac shutdown and will likely crash
#
# \code
# import libgpac as gpac
# gpac.init()
# #create a session in blocking mode
# fs = gpac.FilterSession()
# #setup all your filters
# ...
# #run the session
# fs.run()
# fs.delete()
# gpac.close()
# \endcode
#
# You can specify global options of libgpac and filters (e.g. --opt) by assigning arguments to libgpac:
# \code
# #set global arguments, here inherited from command line
# gpac.set_args(sys.argv)
# \endcode
#
# You can also specify the log levels you want
# \code
# gpac.set_logs("all@info")
# \endcode
# # Session using built-in filters
#
# Filters are loaded as usual in gpac as a string description
# \code
# src = fs.load_src("$URL:opt:opt2=val")
# dst = fs.load_dst("$URL2:opt3:opt4=val")
# f = fs.load("filtername:optX")
# \endcode
#
# By default the filter session will run with implicit linking.
#
# It is possible to assign sources of a filter (much like the `@` command in `gpac`):
# \code
# dst.set_source(f)
# \endcode
#
#
# # Posting user tasks
#
# You can post tasks to the session scheduler to get called back (useful when running the session in blocking mode)
# Tasks must derive FilterTask class and implement their own `execute` method
# \code
# class MyTask(FilterTask):
# def exectute(self):
# do_something()
# #last scheduled task (session is over), abort
# if self.session.last_task:
# return -1
# //keep task active, reschedule in 500 ms
# return 500
#
# task = MyTask('CustomTask')
# fs.post_task(task)
# \endcode
#
# # Creating custom filters
#
# A custom filter allows your application to interact closely with the media pipeline, but cannot be used in graph resolution.
# Custom filters can be sources, sinks, or intermediate filters. The following limitations however exist:
#- custom filters will not be cloned
#- custom filters cannot be used as sources of filters loading a source filter graph dynamically, such as the dashin filter.
#- custom filters cannot be used as destination of filters loading a destination filter graph dynamically, such as the dasher filters.
#
# A custom filter must implement the \ref FilterCustom class, and optionally provide the following methods
# - configure_pid: callback for PID configuration, mandatory if your filter is not a source
# - process: callback for processing
# - process_event: callback for processing and event
# - reconfigure_output: callback for output reconfiguration (PID capability negotiation)
#
# A custom filter must also declare its capabilities, input and output, using push_cap method
# \code
# class MyFilter(FilterTask):
# def __init__(self, session):
# # !! call constructor of parent class first !!
# gpac.FilterCustom.__init__(self, session, "PYnspect")
# #indicate what we accept and produce - this can be done either in the constructor or after, but before calling \ref run or \ref run_step
# #here we indicate we accept anything of type visual, and produce anything of type visual
# self.push_cap("StreamType", "Visual", gpac.GF_CAPS_INPUT_OUTPUT)
#
# #we accept input pids, we must configure them
# #def configure_pid(self, pid, is_remove):
# #do something, return value is a GF_Err value
# #pid being removed
# if is_remove:
# #pid being reconfigured
# elif pid in self.ipids:
# #pid first connection
# else:
#
# #process
# #def process(self):
# #do something, return value is a GF_Err value
#
# #process_event takes a \ref FilterEvent parameter
# #def process_event(self, evt):
# #do something, return value is True (cancelled) or False
#
# #reconfigure_output takes the target output pid as parameter
# #def reconfigure_output(self, opid):
# #do something, return value is a GF_Err value
#
# \endcode
#
# \note
# The \ref FilterCustom object has a list of input and output PIDs created. Before a callback to configure_pid, the input PID is not registered to the list
# of input PIDs if this is the first time the PID is configured.
#
# \code
# src = fs.load_src("$URL:opt:opt2=val")
# dst = fs.load_dst("$URL2:opt3:opt4=val")
# #load a custom filter
# my_filter = MyFilter(fs)
# #make sure dst only consumes from our custom filter
# dst.set_source(f)
# \endcode
# @{
#
##
# \defgroup pycore_grp libgpac core tools
# \ingroup pyapi_grp Python APIs
# \brief Core tools for libgpac.
#
# @{
from ctypes import *
from ctypes.util import find_library
import datetime
import types
import os
import copy
## set to True if numpy was successfully loaded
##\hideinitializer
numpy_support=True
try:
import numpy as np
except ImportError:
numpy_support = False
print("\nWARNING! numpy not present, packet data type is ctypes POINTER(c_ubyte)\n")
##ctypes instance of libgpac
##\hideinitializer
_libgpac=None
## \cond private
#load libgpac
try:
dll_path = find_library("libgpac.dll")
if not dll_path:
raise OSError("No libgpac.dll")
_libgpac = cdll.LoadLibrary(os.path.abspath(dll_path))
except OSError:
try:
_libgpac = cdll.LoadLibrary("libgpac.so")
except OSError:
try:
_libgpac = cdll.LoadLibrary("libgpac.dylib")
except OSError:
print('Failed to locate libgpac (.so/.dll/.dylib) - make sure it is in your system path')
os._exit(1)
#change this to reflect API we encapsulate. An incompatibility in either of these will throw a warning
GF_ABI_MAJOR=16
GF_ABI_MINOR=5
gpac_abi_major=_libgpac.gf_gpac_abi_major()
gpac_abi_minor=_libgpac.gf_gpac_abi_minor()
## \endcond private
## Set to true if mismatch was detected between the ABI version the Python wrapper was designed and the libgpac shared library ABI version
# A warning is thrown if mismatched, but it is left up to the pythin script to decide whether it still wants to use libgpac wrapper
_libgpac_abi_mismatch=False
## \cond private
if (gpac_abi_major != GF_ABI_MAJOR) or (gpac_abi_minor != GF_ABI_MINOR):
abi_mismatch=True
print('WARNING: this python wrapper is for GPAC ABI ' + str(GF_ABI_MAJOR) + '.' + str(GF_ABI_MINOR) + ' but native libgpac ABI is ' + str(gpac_abi_major) + '.' + str(gpac_abi_minor) + '\n\tUndefined behavior or crashes might happen, please update libgpac.py')
## \endcond private
#
# Type definitions
#
##\cond private
_gf_filter_session = c_void_p
_gf_filter = c_void_p
_gf_filter_pid = c_void_p
_gf_filter_packet = c_void_p
_gf_property_entry = c_void_p
_gf_list = c_void_p
gf_bool = c_uint
##\endcond
##\cond private
#error to string helper
_libgpac.gf_error_to_string.argtypes = [c_int]
_libgpac.gf_error_to_string.restype = c_char_p
_libgpac.gf_gpac_version.restype = c_char_p
_libgpac.gf_gpac_copyright.restype = c_char_p
_libgpac.gf_gpac_copyright_cite.restype = c_char_p
_libgpac.gf_sys_set_args.argtypes = [c_int, POINTER(POINTER(c_char))]
_libgpac.gf_sys_init.argtypes = [c_int, c_char_p]
_libgpac.gf_log_set_tools_levels.argtypes = [c_char_p, c_int]
_libgpac.gf_props_get_type_name.argtypes = [c_uint]
_libgpac.gf_props_get_type_name.restype = c_char_p
_libgpac.gf_sys_clock.restype = c_uint
_libgpac.gf_sys_clock_high_res.restype = c_ulonglong
_libgpac.gf_sys_enable_rmtws.argtypes = [gf_bool]
_libgpac.gf_sys_get_rmtws.argtypes = []
_libgpac.gf_sys_get_rmtws.restype = c_void_p
_libgpac.gf_sys_enable_userws.argtypes = [gf_bool]
_libgpac.gf_sys_get_userws.argtypes = []
_libgpac.gf_sys_get_userws.restype = c_void_p
_libgpac.gf_rmt_get_peer_address.argtypes = [c_void_p]
_libgpac.gf_rmt_get_peer_address.restype = c_char_p
_libgpac.gf_rmt_client_send_to_ws.argtypes = [c_void_p, c_void_p, c_uint64, gf_bool]
_libgpac.gf_4cc_to_str.argtypes = [c_uint]
_libgpac.gf_4cc_to_str.restype = c_char_p
_libgpac.gf_4cc_parse.argtypes = [c_char_p]
_libgpac.gf_4cc_parse.restype = c_uint
_libgpac.gf_sleep.argtypes = [c_uint]
_libgpac.gf_sleep.restype = c_uint
_libgpac.gf_props_enum_name.argtypes = [c_uint, c_uint]
_libgpac.gf_props_enum_name.restype = c_char_p
#\endcond
##libgpac version (string)
#\hideinitializer
version = _libgpac.gf_gpac_version().decode("utf-8")
##libgpac copyright notice (string)
#\hideinitializer
copyright = _libgpac.gf_gpac_copyright().decode("utf-8")
##libgpac full copyright notice (string)
#\hideinitializer
copyright_cite = _libgpac.gf_gpac_copyright_cite().decode("utf-8")
## convert error value to string message
# \param err gpac error code (int)
# \return string
def e2s(err):
return _libgpac.gf_error_to_string(err).decode('utf-8')
##\cond private
#default init of libgpac
_libgpac.user_init = False
err = _libgpac.gf_sys_init(0, None)
if err<0:
raise Exception('Failed to initialize libgpac: ' + e2s(err))
#\endcond
## initialize libgpac - see \ref gf_sys_init
# \param mem_track
# \param profile
# \return
#
def init(mem_track=0, profile=None):
if _libgpac.user_init:
return
_libgpac.user_init = True
_libgpac.gf_sys_close()
err = _libgpac.gf_sys_init(mem_track, profile)
if err<0:
raise Exception('Failed to initialize libgpac: ' + e2s(err))
## close libgpac - see \ref gf_sys_close
# \note Make sure you have destroyed all associated gpac resources before calling this !
# \return
#
def close():
_libgpac.gf_sys_close()
if hasattr(_libgpac, 'gf_memory_size'):
if _libgpac.gf_memory_size() or _libgpac.gf_file_handles_count():
set_logs("mem@info")
_libgpac.gf_memory_print()
return 2
return 0
## set log tools and levels - see \ref gf_log_set_tools_levels
# \note Make sure you have destroyed all associated gpac resources before calling this !
# \param logs
# \param reset if true, resets all logs to default
# \return
def set_logs(logs, reset=False):
_libgpac.gf_log_set_tools_levels(logs.encode('utf-8'), reset)
## get clock - see \ref gf_sys_clock
# \return clock in milliseconds
def sys_clock():
return _libgpac.gf_sys_clock()
## get high res clock - see \ref gf_sys_clock_high_res
# \return clock in microseconds
def sys_clock_high_res():
return _libgpac.gf_sys_clock_high_res()
##\cond private
#keep args at libgpac level to avoid python GC
_libgpac._args=None
##\endcond private
## set libgpac arguments - see \ref gf_sys_set_args
# \param args list of strings, the first string is ignored (considered to be the executable name)
# \return
def set_args(args):
_libgpac.user_init = True
nb_args = len(args)
_libgpac._args = (POINTER(c_char)*nb_args)()
for i, arg in enumerate(args):
enc_arg = arg.encode('utf-8')
_libgpac._args[i] = create_string_buffer(enc_arg)
_libgpac.gf_sys_set_args(nb_args, cast(_libgpac._args, POINTER(POINTER(c_char))) )
## enables websocket monitoring server
# \param enable True/False enable or disable server
def enable_rmtws(enable=True):
_libgpac.user_init = True
_libgpac.gf_sys_enable_rmtws(enable)
## enables the user websocket server
# \param enable True/False enable or disable server
def enable_userws(enable=True):
_libgpac.user_init = True
_libgpac.gf_sys_enable_userws(enable)
##\cond private
_libgpac.gf_rmt_client_set_on_del_cbk.argtypes = [c_void_p, py_object, c_void_p]
@CFUNCTYPE(c_int, c_void_p)
def rmt_fun_on_client_close_cbk(_udta):
print("rmt_fun_on_client_close_cbk")
obj = cast(_udta, py_object).value
obj._on_delete()
return 0
##\endcond private
##\cond private
_libgpac.gf_rmt_client_set_on_data_cbk.argtypes = [c_void_p, py_object, c_void_p]
@CFUNCTYPE(c_int, c_void_p, c_void_p, c_uint64, gf_bool)
def rmt_fun_on_client_data_cbk(_udta, data, size, is_binary):
obj = cast(_udta, py_object).value
data = string_at(data, size)
print(f"raw data: {data}")
obj._on_data(data, size, is_binary)
return 0
##\endcond private
## RMTClient object representing a websocket client
# will be passed as parameter on rmt_ws callbacks
class RMTClient():
def __init__(self, handler, client):
self._handler = handler
self._client = client
if hasattr(self._handler, 'on_client_close'):
_libgpac.gf_rmt_client_set_on_del_cbk(self._client, py_object(self), rmt_fun_on_client_close_cbk)
if hasattr(self._handler, 'on_client_data'):
_libgpac.gf_rmt_client_set_on_data_cbk(self._client, py_object(self), rmt_fun_on_client_data_cbk)
def _on_data(self, data, size, is_binary):
if hasattr(self._handler, 'on_client_data'):
if not is_binary:
data = data.decode("utf-8")
self._handler.on_client_data(self, data)
def _on_delete(self):
if hasattr(self._handler, 'on_client_data'):
err = _libgpac.gf_rmt_client_set_on_data_cbk(self._client, py_object(), None)
if hasattr(self._handler, 'on_client_close'):
err = _libgpac.gf_rmt_client_set_on_del_cbk(self._client, py_object(), None)
self._handler.on_client_close(self)
self._client = None
## get the ip+port of the client (can be used as client id)
def peer_address(self):
if self._client:
return _libgpac.gf_rmt_get_peer_address(self._client).decode("utf-8")
pass
## send data to the client on the websocket
def send(self, data):
print(f"client {self._client} sending {data} type {type(data)}")
if self._client:
is_binary = True
if type(data) == str:
data = data.encode('utf-8')
is_binary = False
return _libgpac.gf_rmt_client_send_to_ws(self._client, data, len(data), is_binary)
pass
## RMTHandler object handling the callbacks for rmtws events
#
# to be passed to \ref python.libgpac.libgpac.set_rmt_handler "set_rmt_handler()"
class RMTHandler():
## called when a new client connects to the websocket
# \param client an object of type \ref python.libgpac.libgpac.RMTClient "RMTClient" representing the new client
def on_new_client(self, client: RMTClient):
pass
## called when a client disconnects from the websocket
# \param client an object of type \ref python.libgpac.libgpac.RMTClient "RMTClient" representing the client
def on_client_close(self, client: RMTClient):
pass
## called when a client receives data on its websocket
# \param client an object of type \ref python.libgpac.libgpac.RMTClient "RMTClient" representing the client
# \param data the received data, can be either str or bytes depending on the exchanged data
def on_client_data(self, client: RMTClient, data):
pass
##\cond private
_libgpac.gf_rmt_set_on_new_client_cbk.argtypes = [c_void_p, py_object, c_void_p]
@CFUNCTYPE(c_int, c_void_p, c_void_p)
def rmt_fun_on_new_client_cbk(_udta, client):
obj = cast(_udta, py_object).value
rmt_client = RMTClient(obj, client)
obj.on_new_client(rmt_client)
return 0
##\endcond private
## set the handler for rmt_ws
# \param callback_obj an object of type \ref python.libgpac.libgpac.RMTHandler "RMTHandler" implementing the desired callbacks
def set_rmt_handler(callback_obj):
_libgpac.user_init = True
rmt = _libgpac.gf_sys_get_rmtws()
if hasattr(callback_obj, 'on_new_client'):
err = _libgpac.gf_rmt_set_on_new_client_cbk(rmt, py_object(callback_obj), rmt_fun_on_new_client_cbk)
if err<0:
return False
return True
## set the handler for the user websocket server
# \param callback_obj an object of type \ref python.libgpac.libgpac.RMTHandler "RMTHandler" implementing the desired callbacks
def set_userws_handler(callback_obj):
_libgpac.user_init = True
rmt = _libgpac.gf_sys_get_userws()
if hasattr(callback_obj, 'on_new_client'):
err = _libgpac.gf_rmt_set_on_new_client_cbk(rmt, py_object(callback_obj), rmt_fun_on_new_client_cbk)
if err<0:
return False
return True
## sleep for given time in milliseconds
# \param value time to sleep
# \return
def sleep(value):
_libgpac.gf_sleep(value)
## @}
##
# \defgroup pystruct_grp Structure Wrappers
# \ingroup pyapi_grp
# \brief Python Structures
#
# Python Wrappers for gpac C structures used in this API
#
# @{
## fraction object, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_Fraction
class Fraction(Structure):
## \cond private
def __init(self, num, den):
self.num = num
self.den = den
_fields_ = [ ("num", c_int), ("den", c_uint)]
def __str__(self):
return str(self.num)+'/' + str(self.den)
## \endcond
## large fraction object, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_Fraction64
class Fraction64(Structure):
## \cond private
_fields_ = [("num", c_longlong), ("den", c_ulonglong)]
def __str__(self):
return str(self.num)+'/' + str(self.den)
## \endcond
## filter statistics object, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FilterStats
class FilterStats(Structure):
## \cond private
_fields_ = [
("filter", _gf_filter),
("filter_alias", _gf_filter),
("nb_tasks_done", c_ulonglong),
("nb_pck_processed", c_ulonglong),
("nb_bytes_processed", c_ulonglong),
("nb_pck_sent", c_ulonglong),
("nb_hw_pck_sent", c_ulonglong),
("nb_errors", c_uint),
("nb_current_errors", c_uint),
("nb_bytes_sent", c_ulonglong),
("time_process", c_ulonglong),
("percent", c_int),
("status", c_char_p),
("report_updated", gf_bool),
("name", c_char_p),
("reg_name", c_char_p),
("filter_id", c_char_p),
("done", gf_bool),
("nb_pid_in", c_uint),
("nb_in_pck", c_ulonglong),
("nb_pid_out", c_uint),
("nb_out_pck", c_ulonglong),
("in_eos", gf_bool),
("type", c_int),
("stream_type", c_int),
("codecid", c_int),
("last_ts_sent", Fraction64),
("last_ts_drop", Fraction64),
("last_task_time", c_uint)
]
## \endcond
## filter pid statistics object, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FilterPidStatistics
class FilterPidStatistics(Structure):
## \cond private
_fields_ = [
("disconnected", c_uint),
("average_process_rate", c_uint),
("max_process_rate", c_uint),
("average_bitrate", c_uint),
("max_bitrate", c_uint),
("nb_processed", c_uint),
("max_process_time", c_uint),
("total_process_time", c_ulonglong),
("first_process_time", c_ulonglong),
("last_process_time", c_ulonglong),
("min_frame_dur", c_uint),
("nb_saps", c_uint),
("max_sap_process_time", c_uint),
("total_sap_process_time", c_ulonglong),
("max_buffer_time", c_ulonglong),
("max_playout_time", c_ulonglong),
("min_playout_time", c_ulonglong),
("buffer_time", c_ulonglong),
("nb_buffer_units", c_uint),
("last_rt_report", c_ulonglong),
("rtt", c_uint),
("jitter", c_uint),
("loss_rate", c_uint),
("last_ts_drop", Fraction64),
("last_ts_sent", Fraction64)
]
## \endcond
## filter argument object, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FilterArgs
class FilterArg(Structure):
## \cond private
_fields_ = [
("name", c_char_p),
("reserved", c_int),
("description", c_char_p),
("type", c_int),
("default", c_char_p),
("min_max_enum", c_char_p),
("flags", c_uint)
]
def __str__(self):
res = '- ' + self.name.decode('utf-8') + ' (' + _libgpac.gf_props_get_type_name(self.type).decode('utf-8') + '): ' + self.description.decode('utf-8')
if self.default or self.min_max_enum:
res += ' ('
if self.default:
res += 'default: ' + self.default.decode('utf-8')
if self.min_max_enum:
res += ' min_max_enum: ' + self.min_max_enum.decode('utf-8')
res += ')'
return res
## \endcond
## filter prop type, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_PropVec2i
class PropVec2i(Structure):
## \cond private
_fields_ = [("x", c_int), ("y", c_int)]
def __str__(self):
return str(self.x)+'x' + str(self.y)
## \endcond
## filter prop type, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_PropVec2
class PropVec2(Structure):
## \cond private
_fields_ = [("x", c_double),("y", c_double)]
def __str__(self):
return str(self.x)+'x' + str(self.y)
## \endcond
## filter prop type, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_PropVec3i
class PropVec3i(Structure):
## \cond private
_fields_ = [("x", c_int), ("y", c_int), ("z", c_int)]
def __str__(self):
return str(self.x)+'x' + str(self.y)+'x' + str(self.z)
## \endcond
## filter prop type, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_PropVec4i
class PropVec4i(Structure):
## \cond private
_fields_ = [("x", c_int), ("y", c_int), ("z", c_int), ("w", c_int)]
def __str__(self):
return str(self.x)+'x' + str(self.y)+'x' + str(self.z)+'x' + str(self.w)
## \endcond
## filter prop type, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_PropData
class PropData(Structure):
## \cond private
_fields_ = [("ptr", c_void_p), ("size", c_uint)]
def __str__(self):
return 'data,size:'+str(self.size)
## \endcond
## filter prop type, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_PropStringList
class PropStringList(Structure):
## \cond private
_fields_ = [("vals", POINTER(c_char_p)), ("nb_items", c_uint)]
def __str__(self):
res=''
for i in range(self.nb_items):
if i:
res += ','
res += self.vals[i].decode('utf-8')
return res
## \endcond private
## \cond private
## filter prop type, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_PropUIntList
class PropUIntList(Structure):
_fields_ = [("vals", POINTER(c_uint)), ("nb_items", c_uint)]
def __str__(self):
res=''
for i in range(self.nb_items):
if i:
res += ','
res += str(self.vals[i])
return res
## filter prop type, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_PropIntList
class PropIntList(Structure):
_fields_ = [("vals", POINTER(c_int)), ("nb_items", c_uint)]
def __str__(self):
res=''
for i in range(self.nb_items):
if i:
res += ','
res += str(self.vals[i])
return res
## filter prop type, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_PropVec2iList
class PropVec2iList(Structure):
_fields_ = [("vals", POINTER(PropVec2i)), ("nb_items", c_uint)]
def __str__(self):
res=''
for i in range(self.nb_items):
if i:
res += ','
res += str(self.vals[i].x) + 'x' + str(self.vals[i].y)
return res
## filter property union value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as GF_PropertyValue
class PropertyValueUnion(Union):
_fields_ = [
("longuint", c_ulonglong),
("longsint", c_longlong),
("sint", c_int),
("uint", c_uint),
("boolean", gf_bool),
("frac", Fraction),
("lfrac", Fraction64),
("fnumber", c_float),
("number", c_double),
("vec2i", PropVec2i),
("vec2", PropVec2),
("vec3i", PropVec3i),
("vec4i", PropVec3i),
("data", PropData),
("string", c_char_p),
("ptr", c_void_p),
("string_list", PropStringList),
("uint_list", PropUIntList),
("sint_list", PropIntList),
("v2i_list", PropVec2iList)
]
## filter property value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as GF_PropertyValue
class PropertyValue(Structure):
_fields_ = [
("type", c_uint),
("value", PropertyValueUnion)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_Base
class FEVT_Base(Structure):
## \cond private
_fields_ = [("type", c_uint), ("on_pid", _gf_filter_pid)]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_Play
class FEVT_Play(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("start_range", c_double),
("end_range", c_double),
("speed", c_double),
("from_pck", c_uint),
("hw_buffer_reset", c_ubyte),
("initial_broadcast_play", c_ubyte),
("timestamp_based", c_ubyte),
("full_file_only", c_ubyte),
("forced_dash_segment_switch", c_ubyte),
("drop_non_ref", c_ubyte),
("no_byterange_forward", c_ubyte),
("to_pck", c_uint),
("orig_delay", c_uint),
("hint_first_dts", c_ulonglong),
("hint_start_offset", c_ulonglong),
("hint_end_offset", c_ulonglong)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_SourceSeek
class FEVT_SourceSeek(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("start_offset", c_ulonglong),
("end_offset", c_ulonglong),
("source_switch", c_char_p),
("is_init_segment", c_ubyte),
("skip_cache_expiration", c_ubyte),
("hint_block_size", c_uint)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_SegmentSize
class FEVT_SegmentSize(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("seg_url", c_char_p),
("is_init", gf_bool),
("media_range_start", c_ulonglong),
("media_range_end", c_ulonglong),
("idx_range_start", c_ulonglong),
("idx_range_end", c_ulonglong),
("is_init", c_ubyte),
("is_shift", c_ubyte)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_FragmentSize
class FEVT_FragmentSize(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("is_last", gf_bool),
("offset", c_ulonglong),
("size", c_ulonglong),
("duration", Fraction64),
("independent", gf_bool),
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_AttachScene
class FEVT_AttachScene(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("odm", c_void_p),
("node", c_void_p)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_QualitySwitch
class FEVT_QualitySwitch(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("up", gf_bool),
("dependent_group_index", c_uint),
("q_idx", c_int),
("set_tile_mode_plus_one", c_uint),
("quality_degradation", c_uint)
]
## \endcond
#TODO GF_FEVT_Event
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_FileDelete
class FEVT_FileDelete(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("url", c_char_p)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_VisibilityHint
class FEVT_VisibilityHint(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("min_x", c_uint),
("max_x", c_uint),
("min_y", c_uint),
("max_y", c_uint),
("is_gaze", gf_bool)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_BufferRequirement
class FEVT_BufferRequirement(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("max_buffer_us", c_uint),
("max_playout_us", c_uint),
("min_playout_us", c_uint),
("pid_only", gf_bool)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_TransportHints
class FEVT_TransportHints(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("flags", c_uint),
("seg_duration", Fraction),
("gen_dsi_only", gf_bool),
("wait_seg_boundary", gf_bool)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_NTPRef
class FEVT_NTPRef(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("ntp", c_ulonglong)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_EventMouse
class EVT_mouse(Structure):
## \cond private
_fields_ = [
("type", c_ubyte),
("x", c_int),
("y", c_int),
("wheel", c_float),
("button", c_int),
("keys", c_uint),
("window_id", c_uint)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_EventMultiTouch
class EVT_mtouch(Structure):
## \cond private
_fields_ = [
("type", c_ubyte),
("x", c_float),
("y", c_float),
("rotation", c_float),
("pinch", c_float),
("num_fingers", c_uint),
("window_id", c_uint)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_EventKey
class EVT_keys(Structure):
## \cond private
_fields_ = [
("type", c_ubyte),
("key_code", c_uint),
("hw_code", c_uint),
("flags", c_uint),
("window_id", c_uint)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_EventChar
class EVT_char(Structure):
## \cond private
_fields_ = [
("type", c_ubyte),
("unicode_char", c_uint),
("window_id", c_uint)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_EventSize
class EVT_size(Structure):
## \cond private
_fields_ = [
("type", c_ubyte),
("width", c_uint),
("height", c_uint),
("orientation", c_uint),
("window_id", c_uint)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_EventShow
class EVT_show(Structure):
## \cond private
_fields_ = [
("type", c_ubyte),
("show_type", c_uint),
("window_id", c_uint)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as GF_Event
#only common events from GPAC video ouput are mapped
class EVT_base(Union):
## \cond private
_fields_ = [
("type", c_ubyte),
("mouse", EVT_mouse),
("mtouch", EVT_mtouch),
("keys", EVT_keys),
("char", EVT_char),
("size", EVT_size),
("show", EVT_show)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as \ref GF_FEVT_Event
class FEVT_UserEvent(Structure):
## \cond private
_fields_ = [
("type", c_uint),
("on_pid", _gf_filter_pid),
("event", EVT_base)
]
## \endcond
## event value, as defined in libgpac and usable as a Python object
#Fields have the same types, names and semantics as GF_FilterEvent
class FilterEvent(Union):
## constructor
#\param evt_type type of event
def __init__(self, evt_type=0):
self.base.type = evt_type
## \cond private
_libgpac.user_init = True
_fields_ = [
("base", FEVT_Base),
("play", FEVT_Play),
("seek", FEVT_SourceSeek),
("attach_scene", FEVT_AttachScene),
("user_event", FEVT_UserEvent),
("quality_switch", FEVT_QualitySwitch),
("visibility_hint", FEVT_VisibilityHint),
("buffer_req", FEVT_BufferRequirement),
("seg_size", FEVT_SegmentSize),
("frag_size", FEVT_FragmentSize),
("file_del", FEVT_FileDelete),
("transport_hints", FEVT_TransportHints),
("ntp", FEVT_NTPRef)
]
## \endcond
## Buffer occupancy object
class BufferOccupancy:
##\cond private
def __init__(self, max_units, nb_pck, max_dur, dur, is_final_flush):
##\endcond
##maximum number of packets (partial or full AU) allowed in buffer
self.max_units = max_units
##number of block allowed in buffer
self.nb_pck = nb_pck
##maximum buffer duration in microseconds
self.max_dur = max_dur
## buffer duration in microseconds
self.dur = dur
##if true, the session has been aborted and this is the final flush for this buffer
self.is_final_flush = is_final_flush
## @}
##
# \defgroup pycst_grp Constants
# \ingroup pyapi_grp
# \brief Constants definitions
#
# Python Wrappers for gpac C constants used in this API
#
# @{
# scheduler type definitions
##\hideinitializer
#see \ref GF_FS_SCHEDULER_LOCK_FREE
GF_FS_SCHEDULER_LOCK_FREE=0
##\hideinitializer
##see \ref GF_FS_SCHEDULER_LOCK
GF_FS_SCHEDULER_LOCK=1
##\hideinitializer
##see \ref GF_FS_SCHEDULER_LOCK_FREE_X
GF_FS_SCHEDULER_LOCK_FREE_X=2
##\hideinitializer
##see \ref GF_FS_SCHEDULER_LOCK_FORCE
GF_FS_SCHEDULER_LOCK_FORCE=3
##\hideinitializer
##see \ref GF_FS_SCHEDULER_DIRECT
GF_FS_SCHEDULER_DIRECT=4
#session flags
##\hideinitializer
#see \ref GF_FS_FLAG_LOAD_META
GF_FS_FLAG_LOAD_META=1<<1
##\hideinitializer
#see \ref GF_FS_FLAG_NON_BLOCKING
GF_FS_FLAG_NON_BLOCKING=1<<2
##\hideinitializer
#see \ref GF_FS_FLAG_NO_GRAPH_CACHE
GF_FS_FLAG_NO_GRAPH_CACHE=1<<3
##\hideinitializer
#see \ref GF_FS_FLAG_NO_REGULATION
GF_FS_FLAG_NO_REGULATION=1<<4
##\hideinitializer
#see \ref GF_FS_FLAG_NO_PROBE
GF_FS_FLAG_NO_PROBE=1<<5
##\hideinitializer
#see \ref GF_FS_FLAG_NO_REASSIGN
GF_FS_FLAG_NO_REASSIGN=1<<6
##\hideinitializer
#see \ref GF_FS_FLAG_PRINT_CONNECTIONS
GF_FS_FLAG_PRINT_CONNECTIONS=1<<7
##\hideinitializer
#see \ref GF_FS_FLAG_NO_ARG_CHECK
GF_FS_FLAG_NO_ARG_CHECK=1<<8
##\hideinitializer
#see \ref GF_FS_FLAG_NO_RESERVOIR
GF_FS_FLAG_NO_RESERVOIR=1<<0
##\hideinitializer
#see \ref GF_FS_FLAG_FULL_LINK
GF_FS_FLAG_FULL_LINK=1<<10
##\hideinitializer
#see \ref GF_FS_FLAG_NO_IMPLICIT
GF_FS_FLAG_NO_IMPLICIT=1<<11
##\hideinitializer
#see \ref GF_FS_FLAG_REQUIRE_SOURCE_ID
GF_FS_FLAG_REQUIRE_SOURCE_ID=1<<12
##\hideinitializer
#see \ref GF_FS_FLAG_FORCE_DEFER_LINK
GF_FS_FLAG_FORCE_DEFER_LINK = 1<<13
##\hideinitializer
#see \ref GF_FS_FLAG_PREVENT_PLAY
GF_FS_FLAG_PREVENT_PLAY = 1<<14
##\hideinitializer
#see \ref GF_PROP_FORBIDDEN
GF_PROP_FORBIDDEN=0
##\hideinitializer
#see \ref GF_PROP_SINT
GF_PROP_SINT=1
##\hideinitializer
#see \ref GF_PROP_UINT
GF_PROP_UINT=2
##\hideinitializer
#see \ref GF_PROP_LSINT
GF_PROP_LSINT=3
##\hideinitializer
#see \ref GF_PROP_LUINT
GF_PROP_LUINT=4
##\hideinitializer
#see \ref GF_PROP_BOOL
GF_PROP_BOOL=5
##\hideinitializer
#see \ref GF_PROP_FRACTION
GF_PROP_FRACTION=6
##\hideinitializer
#see \ref GF_PROP_FRACTION64
GF_PROP_FRACTION64=7
##\hideinitializer
#see \ref GF_PROP_FLOAT
GF_PROP_FLOAT=8
##\hideinitializer
#see \ref GF_PROP_DOUBLE
GF_PROP_DOUBLE=9
##\hideinitializer
#see \ref GF_PROP_VEC2I
GF_PROP_VEC2I=10
##\hideinitializer
#see \ref GF_PROP_VEC2
GF_PROP_VEC2=11
##\hideinitializer
#see \ref GF_PROP_VEC3I
GF_PROP_VEC3I=12
##\hideinitializer
#see \ref GF_PROP_VEC4I
GF_PROP_VEC4I=13
##\hideinitializer
#see \ref GF_PROP_STRING
GF_PROP_STRING=14
##\hideinitializer
#see \ref GF_PROP_STRING_NO_COPY
GF_PROP_STRING_NO_COPY=15
##\hideinitializer
#see \ref GF_PROP_DATA
GF_PROP_DATA=16
##\hideinitializer
#see \ref GF_PROP_NAME
GF_PROP_NAME=17
##\hideinitializer
#see \ref GF_PROP_DATA_NO_COPY
GF_PROP_DATA_NO_COPY=18
##\hideinitializer
#see \ref GF_PROP_CONST_DATA
GF_PROP_CONST_DATA=19
##\hideinitializer
#see \ref GF_PROP_POINTER
GF_PROP_POINTER=20
##\hideinitializer
#see \ref GF_PROP_STRING_LIST
GF_PROP_STRING_LIST=21
##\hideinitializer
#see \ref GF_PROP_UINT_LIST
GF_PROP_UINT_LIST=22
##\hideinitializer
#see \ref GF_PROP_SINT_LIST
GF_PROP_SINT_LIST=23
##\hideinitializer
#see \ref GF_PROP_VEC2I_LIST
GF_PROP_VEC2I_LIST=24
##\hideinitializer
#see \ref GF_PROP_4CC
GF_PROP_4CC=25
##\hideinitializer
#see \ref GF_PROP_4CC_LIST
GF_PROP_4CC_LIST=26
##\cond private
GF_PROP_STRING_LIST_COPY=27
##\endcond
##\hideinitializer
#see \ref GF_PROP_FIRST_ENUM
GF_PROP_FIRST_ENUM=40
##\hideinitializer
#see \ref GF_PROP_PIXFMT
GF_PROP_PIXFMT=GF_PROP_FIRST_ENUM
##\hideinitializer
#see \ref GF_PROP_PCMFMT
GF_PROP_PCMFMT=GF_PROP_FIRST_ENUM+1
##\hideinitializer
#see \ref GF_PROP_CICP_COL_PRIM
GF_PROP_CICP_COL_PRIM=GF_PROP_FIRST_ENUM+2
##\hideinitializer
#see \ref GF_PROP_CICP_COL_TFC
GF_PROP_CICP_COL_TFC=GF_PROP_FIRST_ENUM+3
##\hideinitializer
#see \ref GF_PROP_CICP_COL_MX
GF_PROP_CICP_COL_MX=GF_PROP_FIRST_ENUM+4
##\hideinitializer
#see \ref GF_PROP_CICP_LAYOUT
GF_PROP_CICP_LAYOUT=GF_PROP_FIRST_ENUM+5
##\hideinitializer
#see GF_FEVT_PLAY
GF_FEVT_PLAY = 1
##\hideinitializer
#see GF_FEVT_SET_SPEED
GF_FEVT_SET_SPEED = 2
##\hideinitializer
#see GF_FEVT_STOP
GF_FEVT_STOP = 3
##\hideinitializer
#see GF_FEVT_PAUSE
GF_FEVT_PAUSE = 4
##\hideinitializer
#see GF_FEVT_RESUME
GF_FEVT_RESUME = 5
##\hideinitializer
#see GF_FEVT_SOURCE_SEEK
GF_FEVT_SOURCE_SEEK = 6
##\hideinitializer
#see GF_FEVT_SOURCE_SWITCH
GF_FEVT_SOURCE_SWITCH = 7
##\hideinitializer
#see GF_FEVT_SEGMENT_SIZE
GF_FEVT_SEGMENT_SIZE = 8
##\hideinitializer
#see GF_FEVT_ATTACH_SCENE
GF_FEVT_ATTACH_SCENE = 9
##\hideinitializer
#see GF_FEVT_RESET_SCENE
GF_FEVT_RESET_SCENE = 10
##\hideinitializer
#see GF_FEVT_QUALITY_SWITCH
GF_FEVT_QUALITY_SWITCH = 11
##\hideinitializer
#see GF_FEVT_VISIBILITY_HINT
GF_FEVT_VISIBILITY_HINT = 12
##\hideinitializer
#see GF_FEVT_INFO_UPDATE
GF_FEVT_INFO_UPDATE = 13
##\hideinitializer
#see GF_FEVT_BUFFER_REQ
GF_FEVT_BUFFER_REQ = 14
##\hideinitializer
#see GF_FEVT_CAPS_CHANGE
GF_FEVT_CAPS_CHANGE = 15
##\hideinitializer
#see GF_FEVT_CONNECT_FAIL
GF_FEVT_CONNECT_FAIL = 16
##\hideinitializer
#see GF_FEVT_USER
GF_FEVT_USER = 17
##\hideinitializer
#see GF_FEVT_PLAY_HINT
GF_FEVT_PLAY_HINT = 18
##\hideinitializer
#see GF_FEVT_FILE_DELETE
GF_FEVT_FILE_DELETE = 19
##\hideinitializer
#see GF_FS_ARG_HINT_NORMAL
GF_FS_ARG_HINT_NORMAL = 0
##\hideinitializer
#see GF_FS_ARG_HINT_ADVANCED
GF_FS_ARG_HINT_ADVANCED = 1<<1
##\hideinitializer
#see GF_FS_ARG_HINT_EXPERT
GF_FS_ARG_HINT_EXPERT = 1<<2
##\hideinitializer
#see GF_FS_ARG_HINT_HIDE
GF_FS_ARG_HINT_HIDE = 1<<3
##\hideinitializer
#see GF_FS_ARG_UPDATE
GF_FS_ARG_UPDATE = 1<<4
##\hideinitializer
#see GF_FS_ARG_META
GF_FS_ARG_META = 1<<5
##\hideinitializer
#see GF_FS_ARG_META_ALLOC
GF_FS_ARG_META_ALLOC = 1<<6
##\hideinitializer
#see GF_FS_ARG_SINK_ALIAS
GF_FS_ARG_SINK_ALIAS = 1<<7
##\hideinitializer
#see GF_CAPFLAG_IN_BUNDLE
GF_CAPFLAG_IN_BUNDLE = 1
##\hideinitializer
#see GF_CAPFLAG_INPUT
GF_CAPFLAG_INPUT = 1<<1
##\hideinitializer
#see GF_CAPFLAG_OUTPUT
GF_CAPFLAG_OUTPUT = 1<<2
##\hideinitializer
#see GF_CAPFLAG_EXCLUDED
GF_CAPFLAG_EXCLUDED = 1<<3
##\hideinitializer
#see GF_CAPFLAG_LOADED_FILTER
GF_CAPFLAG_LOADED_FILTER = 1<<4
##\hideinitializer
#see GF_CAPFLAG_STATIC
GF_CAPFLAG_STATIC = 1<<5
##\hideinitializer
#see GF_CAPFLAG_OPTIONAL
GF_CAPFLAG_OPTIONAL = 1<<6
##\hideinitializer
#see GF_CAPFLAG_PRESENT
GF_CAPFLAG_PRESENT = 1<<7
##\hideinitializer
#see GF_CAPFLAG_RECONFIG
GF_CAPFLAG_RECONFIG = 1<<8
#helpers
##\hideinitializer
#see GF_CAPS_INPUT
GF_CAPS_INPUT = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_INPUT)
##\hideinitializer
#see GF_CAPS_INPUT_OPT
GF_CAPS_INPUT_OPT = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_INPUT|GF_CAPFLAG_OPTIONAL)
##\hideinitializer
#see GF_CAPS_INPUT_STATIC
GF_CAPS_INPUT_STATIC = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_INPUT|GF_CAPFLAG_STATIC)
##\hideinitializer
#see GF_CAPS_INPUT_STATIC_OPT
GF_CAPS_INPUT_STATIC_OPT = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_INPUT|GF_CAPFLAG_STATIC|GF_CAPFLAG_OPTIONAL)
##\hideinitializer
#see GF_CAPS_INPUT_EXCLUDED
GF_CAPS_INPUT_EXCLUDED = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_INPUT|GF_CAPFLAG_EXCLUDED)
##\hideinitializer
#see GF_CAPS_INPUT_LOADED_FILTER
GF_CAPS_INPUT_LOADED_FILTER = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_INPUT|GF_CAPFLAG_LOADED_FILTER)
##\hideinitializer
#see GF_CAPS_OUTPUT
GF_CAPS_OUTPUT = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_OUTPUT)
##\hideinitializer
#see GF_CAPS_OUTPUT_LOADED_FILTER
GF_CAPS_OUTPUT_LOADED_FILTER = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_OUTPUT|GF_CAPFLAG_LOADED_FILTER)
##\hideinitializer
#see GF_CAPS_OUTPUT_EXCLUDED
GF_CAPS_OUTPUT_EXCLUDED = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_OUTPUT|GF_CAPFLAG_EXCLUDED)
##\hideinitializer
#see GF_CAPS_OUTPUT_STATIC
GF_CAPS_OUTPUT_STATIC = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_OUTPUT|GF_CAPFLAG_STATIC)
##\hideinitializer
#see GF_CAPS_OUTPUT_STATIC_EXCLUDED
GF_CAPS_OUTPUT_STATIC_EXCLUDED = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_OUTPUT|GF_CAPFLAG_EXCLUDED|GF_CAPFLAG_STATIC)
##\hideinitializer
#see GF_CAPS_INPUT_OUTPUT
GF_CAPS_INPUT_OUTPUT = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_INPUT|GF_CAPFLAG_OUTPUT)
##\hideinitializer
#see GF_CAPS_INPUT_OUTPUT_OPT
GF_CAPS_INPUT_OUTPUT_OPT = (GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_INPUT|GF_CAPFLAG_OUTPUT|GF_CAPFLAG_OPTIONAL)
##\hideinitializer
#see GF_STATS_LOCAL
GF_STATS_LOCAL = 0
##\hideinitializer
#see GF_STATS_LOCAL_INPUTS
GF_STATS_LOCAL_INPUTS = 1
##\hideinitializer
#see GF_STATS_DECODER_SINK
GF_STATS_DECODER_SINK = 2
##\hideinitializer
#see GF_STATS_DECODER_SOURCE
GF_STATS_DECODER_SOURCE = 3
##\hideinitializer
#see GF_STATS_ENCODER_SINK
GF_STATS_ENCODER_SINK = 4
##\hideinitializer
#see GF_STATS_ENCODER_SOURCE
GF_STATS_ENCODER_SOURCE = 5
##\hideinitializer
#see GF_STATS_SINK
GF_STATS_SINK = 6
##\hideinitializer
#see \ref GF_SCRIPT_INFO
GF_SCRIPT_INFO = 3
##\hideinitializer
#see \ref GF_PENDING_PACKET
GF_PENDING_PACKET = 2
##\hideinitializer
#see \ref GF_EOS
GF_EOS = 1
##\hideinitializer
#see \ref GF_OK
GF_OK = 0
##\hideinitializer
#see \ref GF_BAD_PARAM
GF_BAD_PARAM = -1
##\hideinitializer
#see \ref GF_OUT_OF_MEM
GF_OUT_OF_MEM = -2
##\hideinitializer
#see \ref GF_IO_ERR
GF_IO_ERR = -3
##\hideinitializer
#see \ref GF_NOT_SUPPORTED
GF_NOT_SUPPORTED = -4
##\hideinitializer
#see \ref GF_CORRUPTED_DATA
GF_CORRUPTED_DATA = -5
##\hideinitializer
#see \ref GF_SCRIPT_ERROR
GF_SCRIPT_ERROR = -8
##\hideinitializer
#see \ref GF_BUFFER_TOO_SMALL
GF_BUFFER_TOO_SMALL = -9
##\hideinitializer
#see \ref GF_NON_COMPLIANT_BITSTREAM
GF_NON_COMPLIANT_BITSTREAM = -10
##\hideinitializer
#see \ref GF_FILTER_NOT_FOUND
GF_FILTER_NOT_FOUND = -11
##\hideinitializer
#see \ref GF_URL_ERROR
GF_URL_ERROR = -12
##\hideinitializer
#see \ref GF_SERVICE_ERROR
GF_SERVICE_ERROR = -13
##\hideinitializer
#see \ref GF_REMOTE_SERVICE_ERROR
GF_REMOTE_SERVICE_ERROR = -14
##\hideinitializer
#see \ref GF_STREAM_NOT_FOUND
GF_STREAM_NOT_FOUND = -15
##\hideinitializer
#see \ref GF_URL_REMOVED
GF_URL_REMOVED = -16
##\hideinitializer
#see \ref GF_IP_ADDRESS_NOT_FOUND
GF_IP_ADDRESS_NOT_FOUND = -40
##\hideinitializer
#see \ref GF_IP_CONNECTION_FAILURE
GF_IP_CONNECTION_FAILURE = -41
##\hideinitializer
#see \ref GF_IP_NETWORK_FAILURE
GF_IP_NETWORK_FAILURE = -42
##\hideinitializer
#see \ref GF_IP_CONNECTION_CLOSED
GF_IP_CONNECTION_CLOSED = -43
##\hideinitializer
#see \ref GF_IP_NETWORK_EMPTY
GF_IP_NETWORK_EMPTY = -44
##\hideinitializer
#see \ref GF_IP_UDP_TIMEOUT
GF_IP_UDP_TIMEOUT = -46
##\hideinitializer
#see \ref GF_AUTHENTICATION_FAILURE
GF_AUTHENTICATION_FAILURE = -50
##\hideinitializer
#see \ref GF_NOT_READY
GF_NOT_READY = -51
##\hideinitializer
#see \ref GF_INVALID_CONFIGURATION
GF_INVALID_CONFIGURATION = -52
##\hideinitializer
#see \ref GF_NOT_FOUND
GF_NOT_FOUND = -53
##\hideinitializer
#see \ref GF_PROFILE_NOT_SUPPORTED
GF_PROFILE_NOT_SUPPORTED = -54
##\hideinitializer
#see \ref GF_REQUIRES_NEW_INSTANCE
GF_REQUIRES_NEW_INSTANCE = -56
##\hideinitializer
#see \ref GF_FILTER_NOT_SUPPORTED
GF_FILTER_NOT_SUPPORTED = -57
##notification is a setup error, the filter chain was never connected
GF_SETUP_ERROR=0
##notification is an error but keep the filter chain connected
GF_NOTIF_ERROR=1
##notification is an error and disconnect the filter chain
GF_NOTIF_ERROR_AND_DISCONNECT=2
##Do not flush session: everything is discarded, potentially breaking output files
GF_FS_FLUSH_NONE=0,
##Flush all pending data before closing sessions: sources will be forced into end of stream and all emitted packets will be processed
GF_FS_FLUSH_ALL=1
##Stop session (resetting buffers) and flush pipeline
GF_FS_FLUSH_FAST=2
## @}
##\cond private
_gf_filter_session = c_void_p
_gf_filter = c_void_p
_gf_filter_pid = c_void_p
#
# filter session bindings
#
_libgpac.gf_fs_new.argtypes = [c_int, c_int, c_int, c_char_p]
_libgpac.gf_fs_new.restype = _gf_filter_session
_libgpac.gf_fs_del.argtypes = [_gf_filter_session]
_libgpac.gf_fs_del.restype = None
_libgpac.gf_fs_run.argtypes = [_gf_filter_session]
_libgpac.gf_fs_load_source.argtypes = [_gf_filter_session, c_char_p, c_char_p, c_char_p, POINTER(c_int)]
_libgpac.gf_fs_load_source.restype = _gf_filter
_libgpac.gf_fs_load_destination.argtypes = [_gf_filter_session, c_char_p, c_char_p, c_char_p, POINTER(c_int)]
_libgpac.gf_fs_load_destination.restype = _gf_filter
_libgpac.gf_fs_load_filter.argtypes = [_gf_filter_session, c_char_p, POINTER(c_int)]
_libgpac.gf_fs_load_filter.restype = _gf_filter
_libgpac.gf_fs_new_filter.argtypes = [_gf_filter_session, c_char_p, c_uint, POINTER(c_int)]
_libgpac.gf_fs_new_filter.restype = _gf_filter
_libgpac.gf_fs_post_user_task.argtypes = [_gf_filter_session, c_void_p, py_object, c_char_p]
_libgpac.gf_fs_post_user_task.restype = c_int
_libgpac.gf_fs_is_last_task.argtypes = [_gf_filter_session]
_libgpac.gf_fs_is_last_task.restype = gf_bool
_libgpac.gf_fs_get_filters_count.argtypes = [_gf_filter_session]
_libgpac.gf_fs_get_filter.argtypes = [_gf_filter_session, c_int]
_libgpac.gf_fs_get_filter.restype = _gf_filter
_libgpac.gf_fs_abort.argtypes = [_gf_filter_session, c_int]
_libgpac.gf_fs_get_http_max_rate.argtypes = [_gf_filter_session]
_libgpac.gf_fs_get_http_rate.argtypes = [_gf_filter_session]
_libgpac.gf_fs_set_http_max_rate.argtypes = [_gf_filter_session, c_uint]
_libgpac.gf_fs_lock_filters.argtypes = [_gf_filter_session, gf_bool]
_libgpac.gf_fs_enable_reporting.argtypes = [_gf_filter_session, gf_bool]
_libgpac.gf_fs_print_stats.argtypes = [_gf_filter_session]
_libgpac.gf_fs_print_connections.argtypes = [_gf_filter_session]
_libgpac.gf_fs_fire_event.argtypes = [_gf_filter_session, _gf_filter, POINTER(FilterEvent), gf_bool]
_libgpac.gf_fs_fire_event.restype = gf_bool
_libgpac.gf_fs_is_supported_mime.argtypes = [_gf_filter_session, c_char_p]
_libgpac.gf_fs_is_supported_mime.restype = gf_bool
_libgpac.gf_fs_is_supported_mime.argtypes = [_gf_filter_session, c_char_p]
_libgpac.gf_fs_is_supported_mime.restype = gf_bool
_libgpac.gf_fs_is_supported_source.argtypes = [_gf_filter_session, c_char_p, c_char_p]
_libgpac.gf_fs_is_supported_source.restype = gf_bool
_libgpac.gf_pixel_fmt_sname.argtypes = [c_uint]
_libgpac.gf_pixel_fmt_sname.restype = c_char_p
_libgpac.gf_audio_fmt_sname.argtypes = [c_uint]
_libgpac.gf_audio_fmt_sname.restype = c_char_p
@CFUNCTYPE(c_int, _gf_filter_session, c_void_p, POINTER(c_uint))
def fs_task_fun(sess, cbk, resched):
obj = cast(cbk, py_object).value
res = obj.execute()
if res==None or res<0:
obj.session.rem_task(obj)
return 0
resched.contents.value=res
return 1
_libgpac.gf_fs_set_filter_creation_callback.argtypes = [_gf_filter_session, c_void_p, py_object, gf_bool]
@CFUNCTYPE(c_int, c_void_p, _gf_filter, gf_bool)
def on_filter_new_del(cbk, _filter, is_del):
sess = cast(cbk, py_object).value
f = sess._to_filter(_filter)
if is_del:
if hasattr(sess, 'on_filter_del'):
sess.on_filter_del(f)
sess._filters.remove(f)
elif hasattr(sess, 'on_filter_new'):
sess.on_filter_new(f)
return 0
_libgpac.gf_fs_set_external_gl_provider.argtypes = [_gf_filter_session, c_void_p, py_object]
@CFUNCTYPE(c_int, c_void_p, gf_bool)
def on_gl_ctx_activate(cbk, set_active):
sess = cast(cbk, py_object).value
if hasattr(sess, 'on_gl_activate'):
sess.on_gl_activate(set_active)
return 0
##\endcond
## Task object for user callbacks from libgpac scheduler
class FilterTask:
## constructor for tasks
#\param name name of the task (used for logging)
def __init__(self, name):
##Task name
#\hideinitializer
self.name = name
##Filter session
#\hideinitializer
self.session = None
## task execution function
#\return -1 or less to remove the task from the scheduler, 0 or more to indicate the number of milliseconds to wait before calling the task again
def execute(self):
return -1
## filter session object - see \ref fs_grp
class FilterSession:
## constructor for filter session - see \ref gf_fs_new
#\param flags session flags (int)
#\param blacklist list of blacklisted filters
#\param nb_threads number of threads to use (int)
#\param sched_type session scheduler type
def __init__(self, flags=0, blacklist=None, nb_threads=0, sched_type=0):
##\cond private
_libgpac.user_init = True
self._sess = None
if blacklist:
self._sess = _libgpac.gf_fs_new(nb_threads, sched_type, flags, blacklist.encode('utf-8'))
else:
self._sess = _libgpac.gf_fs_new(nb_threads, sched_type, flags, None)
if not self._sess:
raise Exception('Failed to create new filter session')
self._filters = []
self._tasks = []
#for now force sync user tasks and filter management to run on main thread, not tested otherwise
_libgpac.gf_fs_set_filter_creation_callback(self._sess, on_filter_new_del, py_object(self), True)
##\endcond
#hack for doxygen to generate member vars (not support for parsing @property)
if 0:
##set to true if this is the last task running, readonly - see \ref gf_fs_is_last_task
self.last_task=0
##number of filters in session, readonly - see \ref gf_fs_get_filters_count
self.nb_filters=0
##current HTTP cumulated download rate, readonly - see \ref gf_fs_get_http_rate
self.http_bitrate=0
##HTTP max download rate - see \ref gf_fs_get_http_max_rate and \ref gf_fs_set_http_max_rate
self.http_max_bitrate=0
## delete an existing filter session - see \ref gf_fs_del
#\warning The filter session must be explicitly destroyed if close (\ref gf_sys_close) is called after that
#\return
def delete(self):
##\cond private
if self._sess:
_libgpac.gf_fs_del(self._sess)
self._sess=None
##\endcond private
## called whenever a new filter is added, typically used by classes deriving from FilterSession
#\param _filter Filter object being added
#\return
def on_filter_new(self, _filter):
pass
## called whenever a filter is destroyed, typically used by classes deriving from FilterSession
#\param _filter Filter object being removed
#\return
def on_filter_del(self, _filter):
pass
## called whenever a GL context must be activated, typically used by classes deriving from FilterSession
#\param do_activate set to true if openGL context ust be activated for calling thread
#\return
def on_gl_activate(self, do_activate):
return 0
## call this function to prevent openGL context creation in libgpac, delegating GL context management to the calling app
#\return
def external_opengl_provider(self):
_libgpac.gf_fs_set_external_gl_provider(self._sess, on_gl_ctx_activate, py_object(self))
##\cond private
def _to_filter(self, f):
for i, a_f in enumerate(self._filters):
if a_f._filter == f:
return a_f
_filter = Filter(self, f)
self._filters.append(_filter)
return _filter
##\endcond private
##run the session - see \ref gf_fs_run
#\return
def run(self):
err = _libgpac.gf_fs_run(self._sess)
if err<0:
raise Exception('Failed to run session: ' + e2s(err))
##load source filter - see \ref gf_fs_load_source
#\param URL source URL to load
#\param parentURL URL of parent resource for relative path resolution
#\return new Filter object
def load_src(self, URL, parentURL=None):
errp = c_int(0)
if parentURL:
_filter = _libgpac.gf_fs_load_source(self._sess, URL.encode('utf-8'), None, parentURL.encode('utf-8'), byref(errp))
else:
_filter = _libgpac.gf_fs_load_source(self._sess, URL.encode('utf-8'), None, None, byref(errp))
err = errp.value
if err<0:
raise Exception('Failed to load source filter ' + URL + ': ' + e2s(err))
return self._to_filter(_filter)
##load destination filter - see \ref gf_fs_load_destination
#\param URL source URL to load
#\param parentURL URL of parent resource for relative path resolution
#\return new Filter object
def load_dst(self, URL, parentURL=None):
errp = c_int(0)
if parentURL:
_filter = _libgpac.gf_fs_load_destination(self._sess, URL.encode('utf-8'), None, parentURL.encode('utf-8'), byref(errp))
else:
_filter = _libgpac.gf_fs_load_destination(self._sess, URL.encode('utf-8'), None, None, byref(errp))
err = errp.value
if err<0:
raise Exception('Failed to load destination filter ' + URL + ': ' + e2s(err))
return self._to_filter(_filter)
##load a filter - see \ref gf_fs_load_filter
#\param fname filter name and options
#\return new Filter object
def load(self, fname):
errp = c_int(0)
_filter = _libgpac.gf_fs_load_filter(self._sess, fname.encode('utf-8'), byref(errp))
err = errp.value
if err<0:
raise Exception('Failed to load filter ' + fname + ': ' + e2s(err))
return self._to_filter(_filter)
##post a user task to the filter sesison - see \ref gf_fs_post_user_task
#\param task task object to post
#\return
def post(self, task):
task.session = self
err = _libgpac.gf_fs_post_user_task(self._sess, fs_task_fun, py_object(task), task.name.encode('utf-8'))
if err<0:
raise Exception('Failed to post task ' + task.name + ': ' + e2s(err))
self._tasks.append(task)
##\cond private
#\return
def rem_task(self, task):
task.session = None
self._tasks.remove(task)
##\endcond
##abort the session - see \ref gf_fs_abort
#\param flush flush pipeline before abort
#\return
def abort(self, flush=0):
_libgpac.gf_fs_abort(self._sess, flush)
##get a filter by index - see \ref gf_fs_get_filter
#\param index index of filter
#\return Filter object
def get_filter(self, index):
f = _libgpac.gf_fs_get_filter(self._sess, index)
if not f:
raise Exception('Failed to get filter at index ' + str(index) )
return self._to_filter(f)
##lock the session - see \ref gf_fs_lock_filters
#\param lock if True, locks otherwise unlocks
#\return
def lock(self, lock):
_libgpac.gf_fs_lock_filters(self._sess, lock)
##enable status reporting by filters - see \ref gf_fs_enable_reporting
#\param do_report if True, enables reporting
#\return
def reporting(self, do_report):
_libgpac.gf_fs_enable_reporting(self._sess, do_report)
##print statistics on stderr - see \ref gf_fs_print_stats
#\return
def print_stats(self):
_libgpac.gf_fs_print_stats(self._sess)
##print graph on stderr - see \ref gf_fs_print_connections
#\return
def print_graph(self):
_libgpac.gf_fs_print_connections(self._sess)
##fire an event on the given filter if any, or on any filter accepting user events
# \param evt FilterEvent to fire
# \param _filter Filter to use as target
# \param upstream if true, walks the chain towards the sink, otehrwise towards the source
#\return
def fire_event(self, evt, _filter=None, upstream=False):
if _filter:
return _libgpac.gf_fs_fire_event(self._sess, _filter._filter, byref(evt), upstream)
return _libgpac.gf_fs_fire_event(self._sess, None, byref(evt), upstream)
##checks if a given mime is supported - see \ref gf_fs_is_supported_mime
# \param mime mime type to check
# \return true or false
def is_supported_mime(self, mime):
return _libgpac.gf_fs_is_supported_mime(self._sess, mime.encode('utf-8'))
##checks if a given source URL is supported - see \ref gf_fs_is_supported_source
# \param url URL to check
# \param parent parent URL for relative URLs
# \return true or false
def is_supported_source(self, url, parent=None):
if parent:
return _libgpac.gf_fs_is_supported_source(self._sess, url.encode('utf-8'), parent.encode('utf-8'))
return _libgpac.gf_fs_is_supported_source(self._sess, url.encode('utf-8'), None)
##\cond private: until end, properties
@property
def last_task(self):
if _libgpac.gf_fs_is_last_task(self._sess):
return True
return False
@property
def nb_filters(self):
return _libgpac.gf_fs_get_filters_count(self._sess)
@property
def http_bitrate(self):
return _libgpac.gf_fs_get_http_rate(self._sess)
@property
def http_max_bitrate(self):
return _libgpac.gf_fs_get_http_max_rate(self._sess)
@http_max_bitrate.setter
def http_max_bitrate(self, value):
_libgpac.gf_fs_set_http_max_rate(self._sess, value)
##\endcond
#TODO
#global session: session caps ?
#missing features available in JSFilterSession API but with no native direct equivalents:
#set_new_filter_fun, set_del_filter_fun, set_event_fun
## \cond private
#
# filter bindings
#
_libgpac.gf_filter_get_name.argtypes = [_gf_filter]
_libgpac.gf_filter_get_name.restype = c_char_p
_libgpac.gf_filter_get_id.argtypes = [_gf_filter]
_libgpac.gf_filter_get_id.restype = c_char_p
_libgpac.gf_filter_get_ipid_count.argtypes = [_gf_filter]
_libgpac.gf_filter_get_opid_count.argtypes = [_gf_filter]
_libgpac.gf_filter_get_status.argtypes = [_gf_filter]
_libgpac.gf_filter_get_status.restype = c_char_p
_libgpac.gf_filter_get_bytes_done.argtypes = [_gf_filter]
_libgpac.gf_filter_get_stats.argtypes = [_gf_filter, POINTER(FilterStats)]
_libgpac.gf_filter_remove.argtypes = [_gf_filter]
_libgpac.gf_fs_send_update.argtypes = [_gf_filter_session, c_char_p, _gf_filter, c_char_p, c_char_p, c_uint]
_libgpac.gf_filter_set_source.argtypes = [_gf_filter, _gf_filter, c_char_p]
_libgpac.gf_filter_set_source_restricted.argtypes = [_gf_filter, _gf_filter, c_char_p]
_libgpac.gf_filter_reconnect_output.argtypes = [_gf_filter, _gf_filter_pid]
_libgpac.gf_props_get_id.argtypes = [c_char_p]
_libgpac.gf_filter_get_ipid.argtypes = [_gf_filter, c_uint]
_libgpac.gf_filter_get_ipid.restype = _gf_filter_pid
_libgpac.gf_filter_get_opid.argtypes = [_gf_filter, c_uint]
_libgpac.gf_filter_get_opid.restype = _gf_filter_pid
_libgpac.gf_filter_pid_get_property.argtypes = [_gf_filter_pid, c_uint]
_libgpac.gf_filter_pid_get_property.restype = POINTER(PropertyValue)
_libgpac.gf_filter_pid_get_property_str.argtypes = [_gf_filter_pid, c_char_p]
_libgpac.gf_filter_pid_get_property_str.restype = POINTER(PropertyValue)
_libgpac.gf_filter_pid_get_info.argtypes = [_gf_filter_pid, c_uint, POINTER(POINTER(_gf_property_entry))]
_libgpac.gf_filter_pid_get_info.restype = POINTER(PropertyValue)
_libgpac.gf_filter_pid_get_info_str.argtypes = [_gf_filter_pid, c_char_p, POINTER(POINTER(_gf_property_entry))]
_libgpac.gf_filter_pid_get_info_str.restype = POINTER(PropertyValue)
_libgpac.gf_filter_release_property.argtypes = [POINTER(_gf_property_entry)]
_libgpac.gf_filter_pid_get_statistics.argtypes = [_gf_filter, POINTER(FilterPidStatistics), c_uint]
_libgpac.gf_props_type_is_enum.argtypes = [c_uint]
_libgpac.gf_props_type_is_enum.restype = c_int
_libgpac.gf_props_parse_enum.argtypes = [c_uint, c_char_p]
_libgpac.gf_props_parse_enum.restype = c_uint
_libgpac.gf_pixel_fmt_name.argtypes = [c_uint]
_libgpac.gf_pixel_fmt_name.restype = c_char_p
_libgpac.gf_audio_fmt_name.argtypes = [c_uint]
_libgpac.gf_audio_fmt_name.restype = c_char_p
_libgpac.gf_filter_pid_enum_properties.argtypes = [_gf_filter_pid, POINTER(c_uint), POINTER(c_uint), POINTER(c_char_p)]
_libgpac.gf_filter_pid_enum_properties.restype = POINTER(PropertyValue)
_libgpac.gf_props_4cc_get_name.argtypes = [c_uint]
_libgpac.gf_props_4cc_get_name.restype = c_char_p
_libgpac.gf_stream_type_name.argtypes = [c_uint]
_libgpac.gf_stream_type_name.restype = c_char_p
_libgpac.gf_codecid_file_ext.argtypes = [c_uint]
_libgpac.gf_codecid_file_ext.restype = c_char_p
_libgpac.gf_filter_pid_get_owner.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_get_owner.restype = _gf_filter
_libgpac.gf_filter_pid_get_source_filter.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_get_source_filter.restype = _gf_filter
_libgpac.gf_filter_pid_enum_destinations.argtypes = [_gf_filter_pid, c_uint]
_libgpac.gf_filter_pid_enum_destinations.restype = _gf_filter
_libgpac.gf_filter_enumerate_args.argtypes = [_gf_filter, c_uint]
_libgpac.gf_filter_enumerate_args.restype = POINTER(FilterArg)
_libgpac.gf_filter_get_info.argtypes = [_gf_filter, c_uint, POINTER(POINTER(_gf_property_entry))]
_libgpac.gf_filter_get_info.restype = POINTER(PropertyValue)
_libgpac.gf_filter_get_info_str.argtypes = [_gf_filter, c_char_p, POINTER(POINTER(_gf_property_entry))]
_libgpac.gf_filter_get_info_str.restype = POINTER(PropertyValue)
_libgpac.gf_filter_get_arg.argtypes = [_gf_filter, c_char_p, POINTER(PropertyValue)]
_libgpac.gf_filter_get_arg.restype = gf_bool
_libgpac.gf_filter_require_source_id.argtypes = [_gf_filter]
_libgpac.gf_filter_probe_link.argtypes = [_gf_filter, c_uint, c_char_p, POINTER(c_char_p)]
_libgpac.gf_filter_get_possible_destinations.argtypes = [_gf_filter, c_int, POINTER(c_char_p)]
_libgpac.gf_httpout_send_request.argtypes = [c_void_p, py_object, c_uint, c_char_p, c_uint, POINTER(POINTER(c_char)), c_void_p, c_void_p, c_void_p, c_void_p]
@CFUNCTYPE(c_uint, c_void_p, c_ulonglong, c_ulonglong)
def httpout_cbk_throttle(cbk, done, total):
obj = cast(cbk, py_object).value
return obj.throttle(done, total)
@CFUNCTYPE(c_int, c_void_p, POINTER(c_ubyte), c_uint)
def httpout_cbk_read(cbk, buf, size):
obj = cast(cbk, py_object).value
if numpy_support:
ar_data = np.ctypeslib.as_array(buf, shape=(size,))
ar_data.flags.writeable=True
return obj.read(ar_data, size)
return obj.read(buf, size)
@CFUNCTYPE(c_uint, c_void_p, POINTER(c_ubyte), c_uint)
def httpout_cbk_write(cbk, buf, size):
obj = cast(cbk, py_object).value
if buf==None:
obj.write(None, 0)
else:
if numpy_support:
ar_data = np.ctypeslib.as_array(buf, shape=(size,))
ar_data.flags.writeable=False
obj.write(ar_data, size)
else:
obj.write(buf, size)
return 0
@CFUNCTYPE(None, c_void_p, c_int)
def httpout_cbk_close(cbk, err):
obj = cast(cbk, py_object).value
obj._root.gc_exclude.remove(obj)
if not obj._skip_close:
obj.close(err)
return 0
## \endcond
##
# \defgroup pyhttpout_grp HTTP server bindings
# \ingroup pyapi_grp
# \brief Python API for libgpac httpout module.
#
# HTTP server requests can be monitored and/or handled in Python. This is achieved by calling \ref Filter.bind on the httpout filter using an object deriving from HTTPOutRequest.
#
# The requests can either be handled by GPAC as usual, or by the python script.
# @{
## HTTP request handler object
class HTTPOutRequest:
## Constructor
def __init__(self):
## \cond private
self._session=None
self._skip_close=0
## \endcond
## reply code
## A reply code of 0 means httpout will serve the resource as usual
self.reply=0
## reply body
## - if reply is 0, this can be set to a file path to serve. If not set, usual URL resolving of httpout is done
## - if reply is not 0, this can be set to a string containing the body. To deliver a binary file or a large file, use read function
self.body=None
## list of headers to add
## even values are header names, odd values are header values
self.headers_out=[]
## throttle the connection - if not overridden by subclass, not used
#\param done amount of bytes of ressource sent
#\param total total size of ressource
#\return a timeout in microseconds, or 0 to process immediately
def throttle(self, done, total):
return 0
## read data for the request - if not overridden by subclass, not used
#\param buf NP array (or c_ubyte pointer if no numpy support) to write data to
#\param size size of array to fill
#\return amount of bytes read, negative value means no data available yet, 0 means end of file
def read(self, buf, size):
return 0
## write data for the request (PUT/POST) - if not overridden by subclass, not used
#\param buf NP array (or c_ubyte pointer if no numpy support) containing data from client
#\param size number of valid bytes in the array
#\return
def write(self, buf, size):
pass
## close callback for the request - if not overridden by subclass, not used
#\param reason GPAC error code of the end of session. If 1 (GF_EOS), the session is ended but underlying network is kept alive, otherwise session is destroyed
#\return
def close(self, reason):
pass
## callback for the request - this shoulld be overridden by subclass, default behaviour being to delegate to GPAC
#\param method HTTP method used, as string
#\param url URL of the HTTP request
#\param auth_code Authentication reply code - requests are pre-identified using GPAC credentials: a value of 401 indicates no identification, 200 indicates identification OK, 403 indicates failure
#\param headers list of headers of input request, even values are header names, odd values are header values
#\return
def on_request(self, method, url, auth_code, headers):
self.send()
## Send the reply to the client. This can be called aither upon \ref python.libgpac.libgpac.HTTPOutRequest.on_request "on_request()" or later (asynchronously)
#\return
def send(self):
hdrs = None
nb_hdrs=len(self.headers_out)
if nb_hdrs:
hdrs = (POINTER(c_char)*nb_hdrs)()
i=0
for hdr in self.headers_out:
hdrs[i] = create_string_buffer(hdr.encode('utf-8'))
i+=1
body = None
if self.body:
body = self.body.encode('utf-8')
ret = _libgpac.gf_httpout_send_request(self._session, py_object(self), self.reply, body, nb_hdrs, hdrs,
None if type(self).throttle == HTTPOutRequest.throttle else httpout_cbk_throttle,
None if type(self).read == HTTPOutRequest.read else httpout_cbk_read,
None if type(self).write == HTTPOutRequest.write else httpout_cbk_write,
httpout_cbk_close
)
## \cond private
self._skip_close = True if type(self).close == HTTPOutRequest.close else False
## \endcond
## @}
## \cond private
_libgpac.gf_filter_bind_httpout_callbacks.argtypes = [_gf_filter, py_object, c_void_p]
@CFUNCTYPE(c_int, c_void_p, c_void_p, c_char_p, c_char_p, c_uint, c_uint, POINTER(c_char_p))
def httpout_on_request(cbk, sess, method, url, auth_code, nb_hdrs, hdrs):
obj = cast(cbk, py_object).value
_ck = obj.__class__
req = _ck()
req._session = sess
req._root = obj
obj.gc_exclude.append(req)
headers=[]
for i in range(nb_hdrs):
headers.append(hdrs[i].decode('utf-8'))
req.on_request(method.decode('utf-8'), url.decode('utf-8'), auth_code, headers)
return 0
_libgpac.gf_filter_bind_dash_algo_callbacks.argtypes = [_gf_filter, py_object, c_void_p, c_void_p, c_void_p, c_void_p]
@CFUNCTYPE(c_int, c_void_p, c_uint)
def dash_period_reset(cbk, reset_type):
obj = cast(cbk, py_object).value
if hasattr(obj, 'on_period_reset'):
obj.on_period_reset(reset_type)
if not reset_type:
obj.groups = []
return 0
class DASHQualityInfoNat(Structure):
_fields_ = [
("bandwidth", c_uint),
("ID", c_char_p),
("mime", c_char_p),
("codec", c_char_p),
("width", c_uint),
("height", c_uint),
("interlaced", gf_bool),
("fps_num", c_uint),
("fps_den", c_uint),
("sar_num", c_uint),
("sar_den", c_uint),
("sample_rate", c_uint),
("nb_channels", c_uint),
("disabled", gf_bool),
("is_selected", gf_bool),
("ast_offset", c_double),
("avg_duration", c_double),
("sizes", _gf_list),
("hls_variant_url", c_char_p),
("ssr", c_uint),
]
class DASHByteRange(Structure):
_fields_ = [
("start", c_ulonglong),
("end", c_ulonglong)
]
class DASHSegURL(Structure):
_fields_ = [
("media", c_char_p),
("media_range", POINTER(DASHByteRange))
#ignore the rest
]
_libgpac.gf_dash_group_get_num_qualities.argtypes = [c_void_p, c_uint]
_libgpac.gf_dash_get_period_duration.argtypes = [c_void_p]
_libgpac.gf_dash_group_get_quality_info.argtypes = [c_void_p, c_uint, c_uint, POINTER(DASHQualityInfoNat) ]
_libgpac.gf_dash_group_get_srd_info.argtypes = [c_void_p, c_uint, POINTER(c_uint), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint) ]
_libgpac.gf_list_count.argtypes = [c_void_p]
_libgpac.gf_list_count.restype = c_uint
_libgpac.gf_list_get.argtypes = [c_void_p, c_uint]
_libgpac.gf_list_get.restype = c_void_p
## \endcond
##
# \defgroup pydash_grp DASH custom algorithm
# \ingroup pyapi_grp
# \brief Python API for libgpac DASH client.
#
# DASH client logic can be controlled in Python. This is achieved by calling \ref Filter.bind on the dashin filter using an object deriving from DASHCustomAlgorithm.
# @{
## DASH media quality information (Representation info)
class DASHQualityInfo:
## \cond private
def __init__(self, qinfon):
## \endcond
## bandwidth in bits per second
self.bandwidth = qinfon.bandwidth
## ID (representation ID in DASH)
self.ID = qinfon.ID.decode('utf-8')
## MIME type
self.mime = qinfon.mime.decode('utf-8')
## codec parameter string
self.codec = qinfon.codec.decode('utf-8')
## width in pixels, 0 if not visual
self.width = qinfon.width
## height in pixels, 0 if not visual
self.height = qinfon.height
## interlaced flag, false 0 if not visual
self.interlaced = qinfon.interlaced
## Frame Rate (Fraction), 0/0 if not visual
self.fps = Fraction(qinfon.fps_num, qinfon.fps_den)
## Sample Aspect Ration (Fraction), 0/0 if not visual
self.sar = Fraction(qinfon.sar_num, qinfon.sar_den)
## Samplerate, 0 if not audio
self.sample_rate = qinfon.sample_rate
## Number of channels, 0 if not audio
self.nb_channels = qinfon.nb_channels
## set to true if quality is disabled (no playback support)
self.disabled = qinfon.disabled
## set to true if quality is selected
self.is_selected = qinfon.is_selected
## AST offset for DASH low latency mode, 0 otherwise
self.ast_offset = qinfon.ast_offset
## Average segment duration in seconds, 0 if unknown
self.avg_duration = qinfon.avg_duration
## HLS variant name
self.hls_variant_url = None
if qinfon.hls_variant_url != None:
self.hls_variant_url = qinfon.hls_variant_url.decode('utf-8')
## SSR representation, estimated number of parts (subsegments), 1 if unknown, 0 if not SSR
self.ssr = qinfon.ssr
## list of segment sizes for VoD cases, None otherwise or if unknown
self.sizes = None
## \cond private
if qinfon.sizes == None:
return
count = _libgpac.gf_list_count(qinfon.sizes)
self.sizes = []
for i in range(count):
surl = cast(_libgpac.gf_list_get(qinfon.sizes, i), POINTER(DASHSegURL)).contents
self.sizes.append( surl.media_range.contents.end - surl.media_range.contents.start + 1)
## \endcond priv
##DASH Spatial Relation Descriptor object, used for tiling
class DASHSRD:
## \cond private
def __init__(self, id, x, y, w, h, fw, fh):
## \endcond
## ID of SRD source - all SRD with same source describe the same video composition, possibly with different grid sizes
self.id = id
## X coordinate of SRD for this tile
self.x = x
## Y coordinate of SRD for this tile
self.y = y
## width of SRD for this tile - 0 for tile base track
self.w = w
## height of SRD for this tile - 0 for tile base track
self.h = h
## total width of SRD descriptor for this tile
self.fw = fw
## total height of SRD descriptor for this tile
self.fh = fh
##\cond private
def make_srd(dashptr, groupidx):
srd_id=c_uint(0)
srd_x=c_uint(0)
srd_y=c_uint(0)
srd_w=c_uint(0)
srd_h=c_uint(0)
srd_fw=c_uint(0)
srd_fh=c_uint(0)
_libgpac.gf_dash_group_get_srd_info(dashptr, groupidx, byref(srd_id), byref(srd_x), byref(srd_y), byref(srd_w), byref(srd_h), byref(srd_fw), byref(srd_fh) )
if not srd_fw.value or not srd_fh.value:
return None
return DASHSRD(srd_id.value, srd_x.value, srd_y.value, srd_w.value, srd_h.value, srd_fw.value, srd_fh.value)
##\endcond
## DASH group object
class DASHGroup:
## \cond private
def __init__(self, ptr_dash, groupidx):
## \endcond
## Index of group, as used in callbacks
self.idx = groupidx
## List of DASHQualityInfo for group
self.qualities = []
## period duration in milliseconds, 0 if unknown
self.duration = _libgpac.gf_dash_get_period_duration(ptr_dash)
## SRD object or None if no SRD defined
self.SRD = make_srd(ptr_dash, groupidx)
## \cond private
self._dash = ptr_dash
nb_qualities = _libgpac.gf_dash_group_get_num_qualities(ptr_dash, groupidx)
for i in range(nb_qualities):
qinfo = DASHQualityInfoNat()
_libgpac.gf_dash_group_get_quality_info(ptr_dash, groupidx, i, byref(qinfo))
self.qualities.append( DASHQualityInfo(qinfo) )
## \endcond
## DASH groups statistics object
class DASHGroupStatistics(Structure):
## \cond private
def __init__(self):
## \endcond
##download rate of last segment in bits per second, divided by current playback speed
self.download_rate = 0
##size of last segment in bytes
self.filesize = 0
##current playback speed
self.speed = 0
##max playback speed based on associated codec runtime statistics
self.max_available_speed = 0
##display width in pixels of object
self.display_width = 0
##display height in pixels of object
self.display_height = 0
##index of current quality or of last downloaded segment quality if previous was skipped
self.active_quality_idx = 0
##minimum buffer in milliseconds, below witch rebuffer occurs
self.buffer_min = 0
##maximum buffer in milliseconds, algorithm should not fill more than this
self.buffer_max = 0
##current buffer in milliseconds
self.buffer = 0
##degradation hint, 0 means no degradation, 100 means tile completely hidden
self.quality_degradation_hint = 0
##cumulated download rate of all active groups in bytes per seconds - 0 means all files are local
self.total_rate = 0
## \cond private
_fields_ = [
("download_rate", c_uint),
("filesize", c_uint),
("speed", c_double),
("max_available_speed", c_double),
("display_width", c_uint),
("display_height", c_uint),
("active_quality_idx", c_uint),
("buffer_min", c_uint),
("buffer_max", c_uint),
("buffer", c_uint),
("degradation_hint", c_uint),
("total_rate", c_uint)
]
def __str__(self):
res = 'active_quality_idx ' + str(self.active_quality_idx)
res += ' rate ' + str(self.download_rate) + ' speed ' + str(self.speed) + ' max_speed ' + str(self.max_available_speed)
res += ' display_width ' + str(self.display_width) + ' display_height ' + str(self.display_height)
res += ' buffer_min ' + str(self.buffer_min) + ' buffer_max ' + str(self.buffer_max) + ' buffer ' + str(self.buffer)
return res
## \endcond
## DASH group current segment download statistics object
class DASHGroupDownloadStatistics(Structure):
## \cond private
def __init__(self):
## \endcond
##download rate of last segment in bits per second
self.bits_per_sec = 0
##total number of bytes in segment
self.total_bytes = 0
##number of downloaded bytes from segment (starting from first byte)
self.bytes_done = 0
##number of microseconds elapsed since segment was scheduled for download
self.time_since_start = 0
##current buffer length in milliseconds
self.buffer_dur = 0
##duration of segment being downloaded, in milliseconds - 0 if unknown
self.current_seg_dur = 0
## \cond private
_fields_ = [
("bits_per_sec", c_uint),
("total_bytes", c_ulonglong),
("bytes_done", c_ulonglong),
("time_since_start", c_ulonglong),
("buffer_dur", c_uint),
("current_seg_dur", c_uint),
]
def __str__(self):
res = 'bits_per_sec ' + str(self.bits_per_sec)
res += ' - total_bytes ' + str(self.total_bytes) + ' bytes_done ' + str(self.bytes_done)
res += ' - time_since_start ' + str(self.time_since_start) + ' us - buffer_dur ' + str(self.buffer_dur) + ' ms - current_seg_dur ' + str(self.current_seg_dur) + ' ms'
return res
## \endcond
## DASH custom algo
# Upon successful binding to the dashin filter, the object will be assigned a list member called `groups`, containing the declared group for the active period
class DASHCustomAlgorithm:
##Callback (optional) called upon a period reset.
#\param reset_type indicate the type of period reset. Values can be:
# - 0: end of period (groups are no longer valid)
# - 1: start of a static period
# - 2: start of a dynamic (live) period
#\return
def on_period_reset(self, reset_type):
pass
##Callback (optional) called when a new group (adaptation set) is created
#\param group the newly created \ref python.libgpac.libgpac.DASHGroup
#\return
def on_new_group(self, group):
pass
##Callback (mandatory) called at the end of the segment download to perform rate adaptation
#\param group the \ref python.libgpac.libgpac.DASHGroup "DASHGroup" on which to perform adaptation
#\param base_group the associated base \ref python.libgpac.libgpac.DASHGroup "DASHGroup" (tiling only), or None if no base group
#\param force_low_complexity indicates that the client would like a lower complexity (typically because it is dropping frames)
#\param stats the \ref python.libgpac.libgpac.DASHGroupStatistics for the downloaded segment
#\return value can be:
# - new quality index,
# - -1 to take no decision
# - -2 to disable quality (debug, will drop segment)
# - other negative values are handled as error
def on_rate_adaptation(self, group, base_group, force_low_complexity, stats):
pass
##Callback (optional) called on regular basis during a segment download
#\param group the \ref python.libgpac.libgpac.DASHGroup "DASHGroup" associated with the current download
#\param stats the \ref python.libgpac.libgpac.DASHGroupDownloadStatistics "DASHGroupDownloadStatistics" for the download
#\return value can be:
# - `-1` to continue download
# - `-2` to abort download but without retrying to downloading the same segment at lower quality
# - the index of the new quality to download for the same segment index (same time)
def on_download_monitor(self, group, stats):
pass
## @}
## \cond private
@CFUNCTYPE(c_int, c_void_p, c_uint, c_void_p)
def dash_group_new(cbk, groupidx, _dashobj):
obj = cast(cbk, py_object).value
if not hasattr(obj, 'on_new_group'):
return 0
new_group = DASHGroup(_dashobj, groupidx)
obj.groups.append(new_group)
obj.on_new_group(new_group)
return 0
@CFUNCTYPE(c_int, c_void_p, c_uint, c_uint, gf_bool, POINTER(DASHGroupStatistics))
def dash_rate_adaptation(cbk, groupidx, base_groupidx, force_low_complex, stats):
obj = cast(cbk, py_object).value
group = None
base_group = None
for i in range(len(obj.groups)):
if obj.groups[i].idx==groupidx:
group = obj.groups[i]
if obj.groups[i].idx==base_groupidx:
base_group = obj.groups[i]
return obj.on_rate_adaptation(group, base_group, force_low_complex, stats.contents)
@CFUNCTYPE(c_int, c_void_p, c_uint, POINTER(DASHGroupDownloadStatistics))
def dash_download_monitor(cbk, groupidx, stats):
obj = cast(cbk, py_object).value
group = None
for i in range(len(obj.groups)):
if obj.groups[i].idx==groupidx:
group = obj.groups[i]
break
return obj.on_download_monitor(group, stats.contents)
def _prop_to_python(pname, prop):
ptype = prop.type
if ptype==GF_PROP_SINT:
return prop.value.sint
if ptype==GF_PROP_UINT:
if pname=="StreamType":
return _libgpac.gf_stream_type_name(prop.value.uint).decode('utf-8')
if pname=="PixelFormat":
return _libgpac.gf_pixel_fmt_sname(prop.value.uint).decode('utf-8')
if pname=="AudioFormat":
return _libgpac.gf_audio_fmt_sname(prop.value.uint).decode('utf-8')
if pname=="CodecID":
cid = _libgpac.gf_codecid_file_ext(prop.value.uint).decode('utf-8')
names=cid.split('|')
return names[0]
return prop.value.uint
if _libgpac.gf_props_type_is_enum(ptype):
pname = _libgpac.gf_props_enum_name(ptype, prop.value.uint)
return pname.decode('utf-8')
if ptype==GF_PROP_4CC:
return _libgpac.gf_4cc_to_str(prop.value.uint).decode('utf-8')
if ptype==GF_PROP_LSINT:
return prop.value.longsint
if ptype==GF_PROP_LUINT:
return prop.value.longuint
if ptype==GF_PROP_BOOL:
return prop.value.boolean
if ptype==GF_PROP_FRACTION:
return prop.value.frac
if ptype==GF_PROP_FRACTION64:
return prop.value.lfrac
if ptype==GF_PROP_FLOAT:
return prop.value.fnumber
if ptype==GF_PROP_DOUBLE:
return prop.value.number
if ptype==GF_PROP_VEC2I:
return prop.value.vec2i
if ptype==GF_PROP_VEC2:
return prop.value.vec2
if ptype==GF_PROP_VEC3I:
return prop.value.vec3i
if ptype==GF_PROP_VEC4I:
return prop.value.vec4i
if ptype==GF_PROP_STRING or ptype==GF_PROP_STRING_NO_COPY or ptype==GF_PROP_NAME:
return prop.value.string.decode('utf-8') if prop.value.string else ""
if ptype==GF_PROP_DATA or ptype==GF_PROP_DATA_NO_COPY or ptype==GF_PROP_CONST_DATA:
return prop.value.data
if ptype==GF_PROP_POINTER:
return prop.value.ptr
if ptype==GF_PROP_STRING_LIST:
res = []
for i in range(prop.value.string_list.nb_items):
val = prop.value.string_list.vals[i].decode('utf-8')
res.append(val)
return res
if ptype==GF_PROP_UINT_LIST:
res = []
for i in range(prop.value.uint_list.nb_items):
val = prop.value.uint_list.vals[i]
res.append(val)
return res
if ptype==GF_PROP_4CC_LIST:
res = []
for i in range(prop.value.uint_list.nb_items):
val = _libgpac.gf_4cc_to_str(prop.value.uint_list.vals[i]).decode('utf-8')
res.append(val)
return res
if ptype==GF_PROP_SINT_LIST:
res = []
for i in range(prop.value.uint_list.nb_items):
val = prop.value.sint_list.vals[i]
res.append(val)
return res
if ptype==GF_PROP_VEC2I_LIST:
res = []
for i in range(prop.value.v2i_list.nb_items):
val = prop.value.v2i_list.vals[i]
res.append(val)
return res
raise Exception('Unknown property type ' + str(ptype))
## \endcond
## filter object
class Filter:
## \cond private
def __init__(self, session, _filter):
self._filter = _filter
self._session = session
## \endcond
#hack for doxygen to generate member vars (not support for parsing @property)
if 0:
##name of the filter, readonly - see \ref gf_filter_get_name
#\hideinitializer
self.name=0
##ID of the filter, readonly - see \ref gf_filter_get_id
#\hideinitializer
self.ID=0
##number of input pids for that filter, readonly - see \ref gf_filter_get_ipid_count
#\hideinitializer
self.nb_ipid=0
##number of output pids for that filter, readonly - see \ref gf_filter_get_opid_count
#\hideinitializer
self.nb_opid=0
##status string for some filters, readonly - see \ref gf_filter_get_status
#\hideinitializer
self.status=0
##bytes processed, readonly - see \ref gf_filter_get_bytes_done
#\hideinitializer
self.bytes_done=0
## \cond private
def __str__(self):
return self.name
## \endcond
## remove this filter - see \ref gf_filter_remove
#\return
def remove(self):
_libgpac.gf_filter_remove(self._filter)
## send option update to this filter - see \ref gf_fs_send_update
#\param name name of option (string)
#\param value value of option (string)
#\param propagate_mask flags indicating if updates must be send to up-chain filters (2), down-chain filters (1), both (3) or only on filter (0)
#\return
def update(self, name, value, propagate_mask=0):
_libgpac.gf_fs_send_update(None, None, self._filter, name.encode('utf-8'), value.encode('utf-8'), propagate_mask)
## set a given filter as source for this filter - see \ref gf_filter_set_source
#
#\note Setting a source will force the filter session linker to run in explicit linking mode.
#\param f source Filter
#\param link_args link options (string)
#\return
def set_source(self, f, link_args=None):
if f:
_libgpac.gf_filter_set_source(self._filter, f._filter, None if not link_args else link_args.encode('utf-8'))
## set a given filter as restricted source for this filter - see \ref gf_filter_set_source_restricted
#\param f source Filter
#\param link_args link options (string)
#\return
def set_source_restricted(self, f, link_args=None):
if f:
_libgpac.gf_filter_set_source_restricted(self._filter, f._filter, None if not link_args else link_args.encode('utf-8'))
## insert a given filter after this filter - see \ref gf_filter_set_source and \ref gf_filter_reconnect_output
#\param f Filter to insert
#\param opid index of output pid to reconnect, -1 for all pids
#\param link_args link options (string)
#\return
def insert(self, f, opid=-1, link_args=None):
if f:
_libgpac.gf_filter_set_source(f._filter, self._filter, None if not link_args else link_args.encode('utf-8'))
pid = None
if opid>=0:
pid = _libgpac.gf_filter_get_opid(self._filter, opid)
if not pid:
raise Exception('No output PID with index ' + str(opid) + ' in filter ' + self.name )
err = _libgpac.gf_filter_reconnect_output(f._filter, pid)
if err<0:
raise Exception('Failed to reconnect output: ' + e2s(err))
## reconnect the filter output - see \ref gf_filter_set_source and \ref gf_filter_reconnect_output
#\param opid index of output pid to reconnect, -1 for all pids
#\return
def reconnect(self, opid=-1):
pid = None
if opid>=0:
pid = _libgpac.gf_filter_get_opid(self._filter, opid)
if not pid:
raise Exception('No output PID with index ' + str(opid) + ' in filter ' + self.name )
err = _libgpac.gf_filter_reconnect_output(self._filter, pid)
if err<0:
raise Exception('Failed to reconnect output: ' + e2s(err))
## \cond private
def _pid_prop_ex(self, prop_name, pid, IsInfo):
_name = prop_name.encode('utf-8')
p4cc = _libgpac.gf_props_get_id(_name)
if IsInfo:
pe = POINTER(_gf_property_entry)()
if p4cc:
prop = _libgpac.gf_filter_pid_get_info(pid, p4cc, byref(pe))
else:
prop = _libgpac.gf_filter_pid_get_info_str(pid, _name, byref(pe))
if prop:
res = _prop_to_python(prop_name, prop.contents)
_libgpac.gf_filter_release_property(pe)
return res
return None
if p4cc:
prop = _libgpac.gf_filter_pid_get_property(pid, p4cc)
else:
prop = _libgpac.gf_filter_pid_get_property_str(pid, _name)
if prop:
return _prop_to_python(prop_name, prop.contents)
else:
pypid = FilterPid(self, pid, None)
if hasattr(pypid, prop_name):
return getattr(pypid, prop_name)
return None
def _pid_prop(self, idx, prop_name, IsInput):
if IsInput:
pid = _libgpac.gf_filter_get_ipid(self._filter, idx)
else:
pid = _libgpac.gf_filter_get_opid(self._filter, idx)
if not pid:
raise Exception('No PID with index ' + str(idx) + ' in filter ' + self.name )
return self._pid_prop_ex(prop_name, pid, False)
def _pid_enum_props_ex(self, callback_obj, _pid):
if hasattr(callback_obj, 'on_prop_enum')==False:
raise Exception('No on_prop_enum function on callback')
pidx=c_uint(0)
while True:
_p4cc=c_uint(0)
_pname=c_char_p(0)
prop = _libgpac.gf_filter_pid_enum_properties(_pid, byref(pidx), byref(_p4cc), byref(_pname))
if prop:
p4cc=_p4cc.value
pname=None
if _pname:
pname=_pname.value.decode('utf-8')
else:
pname=_libgpac.gf_props_4cc_get_name(p4cc).decode('utf-8')
pval = _prop_to_python(pname, prop.contents)
callback_obj.on_prop_enum(pname, pval)
else:
break
def _pid_enum_props(self, idx, callback_obj, IsInput):
if IsInput:
pid = _libgpac.gf_filter_get_ipid(self._filter, idx)
else:
pid = _libgpac.gf_filter_get_opid(self._filter, idx)
if not pid:
raise Exception('No PID with index ' + str(idx) + ' in filter ' + self.name )
self._pid_enum_props_ex(callback_obj, pid)
## \endcond
##get an input pid property by name
#\param idx index of input pid
#\param prop_name name of property
#\return property value or None if not found
def ipid_prop(self, idx, prop_name):
return self._pid_prop(idx, prop_name, True)
##enumerate an input pid properties
#\param idx index of input pid
#\param callback_obj callback object to use, must have a 'on_prop_enum' method defined taking two parameters, prop_name(string) and propval(property value)
#\return
def ipid_enum_props(self, idx, callback_obj):
self._pid_enum_props(idx, callback_obj, True)
##get an output pid property by name
#\param idx index of output pid
#\param prop_name name of property
#\return property value or None if not found
def opid_prop(self, idx, prop_name):
return self._pid_prop(idx, prop_name, False)
##enumerate an output pid properties
#\param idx index of output pid
#\param callback_obj callback object to use, must have a 'on_prop_enum' method defined taking two parameters, prop_name(string) and propval(property value)
#\return
def opid_enum_props(self, idx, callback_obj):
self._pid_enum_props(idx, callback_obj, False)
##Gets the statistics of an input pid of filter - see \ref gf_filter_pid_get_statistics
#\param idx index of input pid
#\param mode search mode for stats cf \ref GF_FilterPidStatsLocation
#\return FilterPidStatistics object
def ipid_stats(self, idx, mode=0):
pid = _libgpac.gf_filter_get_ipid(self._filter, idx)
if not pid:
raise Exception('No PID with index ' + str(idx) + ' in filter ' + self.name )
stats = FilterPidStatistics()
err = _libgpac.gf_filter_pid_get_statistics(pid, byref(stats), mode)
if err<0:
raise Exception('Failed to fetch filter pid stats: ' + e2s(err))
return stats
##Gets the statistics of an output pid of filter - see \ref gf_filter_pid_get_statistics
#\param idx index of output pid
#\param mode search mode for stats cf \ref GF_FilterPidStatsLocation
#\return FilterPidStatistics object
def opid_stats(self, idx, mode=0):
pid = _libgpac.gf_filter_get_opid(self._filter, idx)
if not pid:
raise Exception('No PID with index ' + str(idx) + ' in filter ' + self.name )
stats = FilterPidStatistics()
err = _libgpac.gf_filter_pid_get_statistics(pid, byref(stats), mode)
if err<0:
raise Exception('Failed to fetch filter pid stats: ' + e2s(err))
return stats
##gets the filter at the source of an input pid
#\param idx index of input PID
#\return Filter or None if error
def ipid_source(self, idx):
pid = _libgpac.gf_filter_get_ipid(self._filter, idx)
if not pid:
raise Exception('No PID with index ' + str(idx) + ' in filter ' + self.name )
f = _libgpac.gf_filter_pid_get_source_filter(pid)
if not f:
return None
return self._session._to_filter(f)
##gets the list of destination filters of an output pid
#\param idx index of output PID
#\return list of Filter
def opid_sinks(self, idx):
pid = _libgpac.gf_filter_get_opid(self._filter, idx)
if not pid:
raise Exception('No PID with index ' + str(idx) + ' in filter ' + self.name )
res=[]
f_idx=0
while True:
f = _libgpac.gf_filter_pid_enum_destinations(pid, f_idx)
f_idx+=1
if f==None:
break
_filter = self._session._to_filter(f)
res.append(_filter)
return res
##gets all defined options / arguments for a filter
#\return list of FilterArg
def all_args(self):
res=[]
a_idx=0
while True:
arg = _libgpac.gf_filter_enumerate_args(self._filter, a_idx)
if arg:
arg_val = arg.contents
res.append(arg_val)
else:
break
a_idx+=1
return res
##gets the current value of an argument of the filter - see \ref gf_filter_get_arg
#\param arg_name name of argument (as python str)
#\return argument value or None if not found
def get_arg_value(self, arg_name):
prop = PropertyValue()
res = _libgpac.gf_filter_get_arg(self._filter, arg_name.encode('utf-8'), byref(prop))
if res:
return _prop_to_python(arg_name, prop)
else:
return None
##gets all arguments of filter with type, description, and value as a python dict
#\return array of dictionnary structure containing arguments details
def all_args_value(self):
res = []
for arg in self.all_args():
arg_name = arg.name.decode('utf-8')
argval = self.get_arg_value(arg_name)
res.append( {
'name': arg_name,
'type': _libgpac.gf_props_get_type_name(arg.type).decode('utf-8'),
'value': str(argval),
'description': arg.description.decode('utf-8'),
'default': "" if not arg.default else arg.default.decode('utf-8'),
'min_max_enum': "" if not arg.min_max_enum else arg.min_max_enum.decode('utf-8'),
})
return res
##gets a property info on a filter - see \ref gf_filter_get_info and \ref gf_filter_get_info_str
#\param prop_name property to query
#\return property value or None if not found
def get_info(self, prop_name):
_name = prop_name.encode('utf-8')
p4cc = _libgpac.gf_props_get_id(_name)
pe = POINTER(_gf_property_entry)()
if p4cc:
prop = _libgpac.gf_filter_get_info(self._filter, p4cc, byref(pe))
else:
prop = _libgpac.gf_filter_get_info_str(self._filter, _name, byref(pe))
if prop:
res = _prop_to_python(prop_name, prop.contents)
_libgpac.gf_filter_release_property(pe)
return res
return None
##Gets the statistics of a filter - see \ref gf_filter_get_stats
#\return FilterStats object
def get_statistics(self):
stats = FilterStats()
err = _libgpac.gf_filter_get_stats(self._filter, byref(stats))
if err<0:
raise Exception('Failed to fetch filter stats: ' + e2s(err))
return stats
##enforces sourceID to be present for output pids of this filter - see \ref gf_filter_require_source_id
#\return
def require_source_id(self):
_libgpac.gf_filter_require_source_id(self._filter)
return
##Resolves link from given output pid of filter to a filter description. The described filter is not loaded in the graph - see \ref gf_filter_probe_link
#\param opid_idx 0-based index of the output pid
#\param name filter description to link to - this can be any filter description
#\return None if no possible link or a list containing the filters in the resolved chain from current filter to destination
def probe_link(self, opid_idx, name):
links = c_char_p(0)
ret = _libgpac.gf_filter_probe_link(self._filter, opid_idx, name.encode("utf-8"), byref(links))
if ret<0:
return None
if not links:
return None
link_list = links.value.decode('utf-8').split(",")
_libgpac.gf_url_free(links)
return link_list
##Gets all possible destination filter for this filter or one of its output PID - see \ref gf_filter_get_possible_destinations
#\param opid_idx 0-based index of the output pid, use -1 to check all output pids
#\return None if no possible connections to known filter, or a list containing all possible direct connections
def get_destinations(self, opid_idx):
dests = c_char_p(0)
ret = _libgpac.gf_filter_get_possible_destinations(self._filter, opid_idx, byref(dests))
if ret<0:
return None
if not dests:
return None
dests_list = dests.value.decode('utf-8').split(",")
_libgpac.gf_url_free(dests)
return dests_list
##\cond private
def _bind_dash_algo(self, object):
if not hasattr(object, 'on_rate_adaptation'):
raise Exception('Missing on_rate_adaptation member function on object, cannot bind')
object.groups = []
if hasattr(object, 'on_download_monitor'):
err = _libgpac.gf_filter_bind_dash_algo_callbacks(self._filter, py_object(object), dash_period_reset, dash_group_new, dash_rate_adaptation, dash_download_monitor)
else:
err = _libgpac.gf_filter_bind_dash_algo_callbacks(self._filter, py_object(object), dash_period_reset, dash_group_new, dash_rate_adaptation, None)
if err<0:
raise Exception('Failed to bind dash algo: ' + e2s(err))
return 0
def _bind_httpout(self, object):
if not hasattr(object, 'on_request'):
raise Exception('Missing on_request member function on object, cannot bind')
object.sessions = []
err = _libgpac.gf_filter_bind_httpout_callbacks(self._filter, py_object(object), httpout_on_request)
if err<0:
raise Exception('Failed to bind httpout: ' + e2s(err))
object.gc_exclude=[]
return 0
##\endcond private
## \brief binds a given object to the filter
#
#Binds the given object to the underlying filter for callbacks override - only supported by DASH demuxer for the current time
#
#For DASH, the object must derive from or implement the methods of the \ref python.libgpac.libgpac.DASHCustomAlgorithm "DASHCustomAlgorithm" class:
#
#\param object object to bind
#\return
def bind(self, object):
if self.name=="dashin":
return self._bind_dash_algo(object)
if self.name=="httpout":
return self._bind_httpout(object)
raise Exception('No possible binding to filter class ' + self.name)
##\cond private: until end, properties
@property
def name(self):
return _libgpac.gf_filter_get_name(self._filter).decode('utf-8')
@property
def ID(self):
f_id = _libgpac.gf_filter_get_id(self._filter);
if not f_id:
return None
return f_id.decode('utf-8')
@property
def nb_ipid(self):
return _libgpac.gf_filter_get_ipid_count(self._filter)
@property
def nb_opid(self):
return _libgpac.gf_filter_get_opid_count(self._filter)
@property
def status(self):
return _libgpac.gf_filter_get_status(self._filter).decode('utf-8')
@property
def bytes_done(self):
return _libgpac.gf_filter_get_bytes_done(self._filter)
##\endcond
##\cond private
#
# custom filter bindings
#
_libgpac.gf_filter_push_caps.argtypes = [_gf_filter, c_uint, POINTER(PropertyValue), c_char_p, c_uint, c_uint]
_libgpac.gf_props_4cc_get_type.argtypes = [c_uint]
_libgpac.gf_stream_type_by_name.argtypes = [c_char_p]
_libgpac.gf_stream_type_by_name.restype = c_uint
_libgpac.gf_codecid_parse.argtypes = [c_char_p]
_libgpac.gf_codecid_parse.restype = c_uint
_libgpac.gf_pixel_fmt_parse.argtypes = [c_char_p]
_libgpac.gf_pixel_fmt_parse.restype = c_uint
_libgpac.gf_audio_fmt_parse.argtypes = [c_char_p]
_libgpac.gf_audio_fmt_parse.restype = c_uint
_libgpac.gf_cicp_color_primaries_name.argtypes = [c_uint]
_libgpac.gf_cicp_color_primaries_name.restype = c_char_p
_libgpac.gf_cicp_color_transfer_name.argtypes = [c_uint]
_libgpac.gf_cicp_color_transfer_name.restype = c_char_p
_libgpac.gf_cicp_color_matrix_name.argtypes = [c_uint]
_libgpac.gf_cicp_color_matrix_name.restype = c_char_p
_libgpac.gf_cicp_parse_color_primaries.argtypes = [c_char_p]
_libgpac.gf_cicp_parse_color_primaries.restype = c_uint
_libgpac.gf_cicp_parse_color_transfer.argtypes = [c_char_p]
_libgpac.gf_cicp_parse_color_transfer.restype = c_uint
_libgpac.gf_cicp_parse_color_matrix.argtypes = [c_char_p]
_libgpac.gf_cicp_parse_color_matrix.restype = c_uint
def check_integer(val):
return (isinstance(val,float) and val%1==val) or isinstance(val,int)
def _make_prop(prop4cc, propname, prop, custom_type=0):
prop_val = PropertyValue()
if prop4cc==0:
if not custom_type:
prop_val.type = GF_PROP_STRING
prop_val.value.string = str(prop).encode('utf-8')
return prop_val
ptype = custom_type
else:
ptype = _libgpac.gf_props_4cc_get_type(prop4cc)
prop_val.type = ptype
if propname=="StreamType":
prop_val.value.uint = _libgpac.gf_stream_type_by_name(prop.encode('utf-8'))
return prop_val
elif propname=="CodecID":
prop_val.value.uint = _libgpac.gf_codecid_parse(prop.encode('utf-8'))
return prop_val
elif propname=="PixelFormat":
prop_val.value.uint = _libgpac.gf_pixel_fmt_parse(prop.encode('utf-8'))
return prop_val
elif propname=="AudioFormat":
prop_val.value.uint = _libgpac.gf_audio_fmt_parse(prop.encode('utf-8'))
return prop_val
if ptype==GF_PROP_SINT:
prop_val.value.sint = prop
elif ptype==GF_PROP_UINT:
prop_val.value.uint = prop
elif ptype==GF_PROP_4CC:
prop_val.value.uint = _libgpac.gf_4cc_parse(prop.encode('utf-8'))
elif ptype==GF_PROP_LSINT:
prop_val.value.longsint = prop
elif ptype==GF_PROP_LUINT:
prop_val.value.longuint = prop
elif ptype==GF_PROP_BOOL:
prop_val.value.boolean = prop
elif ptype==GF_PROP_FRACTION:
if hasattr(prop, 'den') and hasattr(prop, 'num'):
prop_val.value.frac.num = int(prop.num)
prop_val.value.frac.den = int(prop.den)
elif check_integer(prop):
prop_val.value.frac.num = int(prop)
prop_val.value.frac.den = 1
else:
prop_val.value.frac.num = int(1000*prop)
prop_val.value.frac.den = 1000
elif ptype==GF_PROP_FRACTION64:
if hasattr(prop, 'den') and hasattr(prop, 'num'):
prop_val.value.lfrac.num = int(prop.num)
prop_val.value.lfrac.den = int(prop.den)
elif check_integer(prop):
prop_val.value.lfrac.num = int(prop)
prop_val.value.lfrac.den = 1
else:
prop_val.value.lfrac.num = int(1000000*prop)
prop_val.value.lfrac.den = 1000000
elif ptype==GF_PROP_FLOAT:
prop_val.value.fnumber = prop
elif ptype==GF_PROP_DOUBLE:
prop_val.value.number = prop
elif ptype==GF_PROP_VEC2I:
if hasattr(prop, 'x') and hasattr(prop, 'y'):
prop_val.value.vec2i.x = prop.x
prop_val.value.vec2i.y = prop.y
else:
raise Exception('Invalid property value for vec2i: ' + str(prop))
elif ptype==GF_PROP_VEC2:
if hasattr(prop, 'x') and hasattr(prop, 'y'):
prop_val.value.vec2.x = prop.x
prop_val.value.vec2.y = prop.y
else:
raise Exception('Invalid property value for vec2: ' + str(prop))
elif ptype==GF_PROP_VEC3I:
if hasattr(prop, 'x') and hasattr(prop, 'y') and hasattr(prop, 'z'):
prop_val.value.vec3i.x = prop.x
prop_val.value.vec3i.y = prop.y
prop_val.value.vec3i.z = prop.z
else:
raise Exception('Invalid property value for vec3i: ' + str(prop))
elif ptype==GF_PROP_VEC4I:
if hasattr(prop, 'x') and hasattr(prop, 'y') and hasattr(prop, 'z') and hasattr(prop, 'w'):
prop_val.value.vec4i.x = prop.x
prop_val.value.vec4i.y = prop.y
prop_val.value.vec4i.z = prop.z
prop_val.value.vec4i.w = prop.w
else:
raise Exception('Invalid property value for vec4i: ' + str(prop))
elif ptype==GF_PROP_STRING or ptype==GF_PROP_STRING_NO_COPY or ptype==GF_PROP_NAME:
prop_val.value.string = str(prop).encode('utf-8')
elif ptype==GF_PROP_DATA or ptype==GF_PROP_DATA_NO_COPY or ptype==GF_PROP_CONST_DATA:
raise Exception('Setting data property from python not yet implemented !')
elif ptype==GF_PROP_POINTER:
raise Exception('Setting pointer property from python not yet implemented !')
elif ptype==GF_PROP_STRING_LIST:
if isinstance(prop, list)==False:
raise Exception('Property is not a list')
#force copy of prop
prop_val.type = GF_PROP_STRING_LIST_COPY
nb_vals = len(prop)
prop_val.value.string_list.nb_items = nb_vals
prop_val.value.string_list.vals = cast((POINTER(c_char) * nb_vals)(), POINTER(c_char_p))
for i, s_val in enumerate(prop):
prop_val.value.string_list.vals[i] = cast(create_string_buffer(s_val.encode('utf-8')), c_char_p)
elif ptype==GF_PROP_UINT_LIST:
if isinstance(prop, list)==False:
raise Exception('Property is not a list')
nb_vals = len(prop)
prop_val.value.uint_list.nb_items = nb_vals
prop_val.value.uint_list.vals = (c_uint * nb_vals)(*prop)
elif ptype==GF_PROP_4CC_LIST:
if isinstance(prop, list)==False:
raise Exception('Property is not a list')
nb_vals = len(prop)
prop_val.value.uint_list.nb_items = nb_vals
prop_val.value.uint_list.vals = (c_uint * nb_vals)()
for i, s_val in enumerate(prop):
prop_val.value.uint_list.vals[i] = _libgpac.gf_4cc_parse( s_val.encode('utf-8') )
elif ptype==GF_PROP_SINT_LIST:
if isinstance(prop, list)==False:
raise Exception('Property is not a list')
nb_vals = len(prop)
prop_val.value.sint_list.nb_items = nb_vals
prop_val.value.sint_list.vals = (c_int * nb_vals)(*prop)
elif ptype==GF_PROP_VEC2I_LIST:
if isinstance(prop, list)==False:
raise Exception('Property is not a list')
nb_vals = len(prop)
prop_val.value.sint_list.nb_items = nb_vals
prop_val.value.v2i_list.vals = (PropVec2i * nb_vals)()
for i, vec in enumerate (prop):
print('obj is ' + str(vec) )
prop_val.value.v2i_list.vals[i].x = vec.x
prop_val.value.v2i_list.vals[i].y = vec.y
elif _libgpac.gf_props_type_is_enum(ptype):
prop_val.value.uint = _libgpac.gf_props_parse_enum(ptype, prop.encode('utf-8'))
return prop_val
else:
raise Exception('Unsupported property type ' + str(ptype) )
return prop_val
_libgpac.gf_filter_set_rt_udta.argtypes = [_gf_filter, py_object]
_libgpac.gf_filter_get_rt_udta.argtypes = [_gf_filter]
_libgpac.gf_filter_get_rt_udta.restype = c_void_p
_libgpac.gf_filter_pid_set_udta.argtypes = [_gf_filter, py_object]
_libgpac.gf_filter_pid_get_udta.argtypes = [_gf_filter]
_libgpac.gf_filter_pid_get_udta.restype = c_void_p
_libgpac.gf_filter_pid_send_event.argtypes = [_gf_filter_pid, POINTER(FilterEvent)]
_libgpac.gf_filter_set_configure_ckb.argtypes = [_gf_filter, c_void_p]
@CFUNCTYPE(c_int, _gf_filter, _gf_filter_pid, gf_bool)
def filter_cbk_configure(_f, _pid, is_remove):
obj = _libgpac.gf_filter_get_rt_udta(_f)
_filter = cast(obj, py_object).value
obj = _libgpac.gf_filter_pid_get_udta(_pid)
if obj:
pid_obj = cast(obj, py_object).value
else:
pid_obj=None
first=True
if pid_obj:
first=False
else:
pid_obj = FilterPid(_filter, _pid, True)
_libgpac.gf_filter_pid_set_udta(_pid, py_object(pid_obj))
res = _filter.configure_pid(pid_obj, is_remove)
if is_remove:
_libgpac.gf_filter_pid_set_udta(_pid, None)
if pid_obj in _filter.ipids:
_filter.ipids.remove(pid_obj)
return res
if first:
_filter.ipids.append(pid_obj)
return res
_libgpac.gf_filter_set_process_ckb.argtypes = [_gf_filter, c_void_p]
@CFUNCTYPE(c_int, _gf_filter)
def filter_cbk_process(_f):
obj = _libgpac.gf_filter_get_rt_udta(_f)
_filter = cast(obj, py_object).value
return _filter.process()
_libgpac.gf_filter_set_process_event_ckb.argtypes = [_gf_filter, c_void_p]
@CFUNCTYPE(c_int, _gf_filter, POINTER(FilterEvent) )
def filter_cbk_process_event(_f, _evt):
obj = _libgpac.gf_filter_get_rt_udta(_f)
_filter = cast(obj, py_object).value
res = _filter.process_event(_evt.contents)
if res:
return 1
return 0
_libgpac.gf_filter_set_reconfigure_output_ckb.argtypes = [_gf_filter, c_void_p]
@CFUNCTYPE(c_int, _gf_filter, _gf_filter_pid )
def filter_cbk_reconfigure_output(_f, _pid):
obj = _libgpac.gf_filter_get_rt_udta(_f)
_filter = cast(obj, py_object).value
obj = _libgpac.gf_filter_pid_get_udta(_pid)
if obj:
pid_obj = cast(obj, py_object).value
else:
pid_obj=None
if pid_obj:
return _filter.reconfigure_output(_f)
raise Exception('Reconfigure on unknown output pid !')
_libgpac.gf_filter_pid_get_packet.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_get_packet.restype = _gf_filter_packet
_libgpac.gf_filter_pid_drop_packet.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_new.argtypes = [_gf_filter]
_libgpac.gf_filter_pid_new.restype = _gf_filter_pid
_libgpac.gf_filter_update_status.argtypes = [_gf_filter, c_uint, c_char_p]
_libgpac.gf_filter_ask_rt_reschedule.argtypes = [_gf_filter, c_uint]
_libgpac.gf_filter_post_process_task.argtypes = [_gf_filter]
_libgpac.gf_filter_setup_failure.argtypes = [_gf_filter, c_int]
_libgpac.gf_filter_make_sticky.argtypes = [_gf_filter]
_libgpac.gf_filter_prevent_blocking.argtypes = [_gf_filter, gf_bool]
_libgpac.gf_filter_block_eos.argtypes = [_gf_filter, gf_bool]
_libgpac.gf_filter_set_max_extra_input_pids.argtypes = [_gf_filter, c_uint]
_libgpac.gf_filter_block_enabled.argtypes = [_gf_filter]
_libgpac.gf_filter_block_enabled.restype = gf_bool
_libgpac.gf_filter_get_output_buffer_max.argtypes = [_gf_filter, POINTER(c_uint), POINTER(c_uint)]
_libgpac.gf_filter_all_sinks_done.argtypes = [_gf_filter]
_libgpac.gf_filter_all_sinks_done.restype = gf_bool
_libgpac.gf_filter_get_num_events_queued.argtypes = [_gf_filter]
_libgpac.gf_filter_get_clock_hint.argtypes = [_gf_filter, POINTER(c_ulonglong), POINTER(Fraction64)]
_libgpac.gf_filter_connections_pending.argtypes = [_gf_filter]
_libgpac.gf_filter_connections_pending.restype = gf_bool
_libgpac.gf_filter_hint_single_clock.argtypes = [_gf_filter, c_ulonglong, Fraction64]
GF_FS_REG_MAIN_THREAD=1<<1
##\endcond
## Base class used to create custom filters in python
class FilterCustom(Filter):
##constructor, see \ref gf_fs_new_filter
#\param session FilterSession object
#\param fname name of the filter
#\param flags flags for the filter
def __init__(self, session, fname="Custom", flags=0):
errp = c_int(0)
_filter = _libgpac.gf_fs_new_filter(session._sess, fname.encode('utf-8'), flags | GF_FS_REG_MAIN_THREAD, byref(errp))
err = errp.value
if err<0:
raise Exception('Failed to create filter ' + fname + ': ' + e2s(err))
#create base class
Filter.__init__(self, session, _filter)
#filter is registered with our filter bank (always done in callback from fs_new_filter) as a generic gpac.Filter object
#remove it and add this new custom filter
#we browse the list for safety, although with current code the new gpac.Filter object is always the last one, so pop() would be enough
for f in session._filters:
if self._filter != f._filter:
continue
session._filters.remove(f)
session._filters.append(self)
break
#setup callback udta and callback functions
_libgpac.gf_filter_set_rt_udta(_filter, py_object(self) )
if hasattr(self, 'configure_pid'):
_libgpac.gf_filter_set_configure_ckb(self._filter, filter_cbk_configure)
if hasattr(self, 'process'):
_libgpac.gf_filter_set_process_ckb(self._filter, filter_cbk_process)
if hasattr(self, 'process_event'):
_libgpac.gf_filter_set_process_event_ckb(self._filter, filter_cbk_process_event)
if hasattr(self, 'reconfigure_output'):
_libgpac.gf_filter_set_reconfigure_output_ckb(self._filter, filter_cbk_reconfigure_output)
##List of input FilterPid
self.ipids=[]
##List of output FilterPid
self.opids=[]
#hack for doxygen to generate member vars (not support for parsing @property)
if 0:
##filter blocking is enabled, readonly - see \ref gf_filter_block_enabled
#\hideinitializer
self.block_enabled=0
##maximum output buffer time, readonly - see \ref gf_filter_get_output_buffer_max
#\hideinitializer
self.output_buffer=0
##maximum plyaout buffer time, readonly - see \ref gf_filter_get_output_buffer_max
#\hideinitializer
self.playout_buffer=0
##all sinks are done for this filter, readonly - see \ref gf_filter_all_sinks_done
#\hideinitializer
self.sinks_done=0
##number of queued events on the filter, readonly - see \ref gf_filter_get_num_events_queued
#\hideinitializer
self.nb_evts_queued=0
##clock hint value in microseconds, readonly - see \ref gf_filter_get_clock_hint
#\hideinitializer
self.clock_hint_time=0
##clock hint media time as Fraction64, readonly - see \ref gf_filter_get_clock_hint
#\hideinitializer
self.clock_hint_mediatime=0
##boolean indicating connections are pending on the filter, readonly - see \ref gf_filter_connections_pending
#\hideinitializer
self.connections_pending=0
##push a capability in the current capability bundle - see \ref gf_filter_push_caps
#\param pcode capability name (property type)
#\param prop capability value
#\param flag capability flags (input, output, etc)
#\param priority capability priority
#\param custom_type type of property if user-defined property. If not set and user-defined, property is a string
#\return
def push_cap(self, pcode, prop, flag, priority=0, custom_type=0):
prop_4cc = pcode
prop_name = None
if isinstance(pcode, str):
_pname = pcode.encode('utf-8')
prop_4cc = _libgpac.gf_props_get_id(_pname)
if prop_4cc==0:
prop_name = _pname
prop_val = _make_prop(prop_4cc, pcode, prop, custom_type)
err = _libgpac.gf_filter_push_caps(self._filter, prop_4cc, byref(prop_val), prop_name, flag, priority)
if err<0:
raise Exception('Failed to push cap: ' + e2s(err))
##create a new output pid for this filter - see \ref gf_filter_pid_new
#\return FilterPid object
def new_pid(self):
_pid = _libgpac.gf_filter_pid_new(self._filter)
if _pid:
pid_obj = FilterPid(self, _pid, False)
_libgpac.gf_filter_pid_set_udta(_pid, py_object(pid_obj))
self.opids.append(pid_obj)
pid_obj.pck_refs = []
return pid_obj
raise Exception('Failed to create output pid')
##update filter status - see \ref gf_filter_update_status
#\param status status string
#\param percent progress in per 10000, int
#\return
def update_status(self, status, percent):
_libgpac.gf_filter_update_status(self._filter, percent, status.encode('utf-8'))
##reschedule the filter after a given delay - see \ref gf_filter_ask_rt_reschedule and \ref gf_filter_post_process_task
#\param when delay in microseconds, negative value will just post a process task. A value of 0 will mark filter as active even if no packet was dropped/sent
#\return
def reschedule(self, when=0):
if when>=0:
_libgpac.gf_filter_ask_rt_reschedule(self._filter, when)
else:
_libgpac.gf_filter_post_process_task(self._filter)
##notify an internal failure of the filter has happend - see \ref gf_filter_notification_failure and \ref gf_filter_setup_failure
#\param err the failure reason (gpac error code, int)
#\param error_type the failure notification type
#\return
def notify_failure(self, err, error_type=GF_SETUP_ERROR):
if error_type==GF_NOTIF_ERROR:
_libgpac.gf_filter_notification_failure(self._filter, err, False)
elif error_type==GF_NOTIF_ERROR_AND_DISCONNECT:
_libgpac.gf_filter_notification_failure(self._filter, err, True)
else:
_libgpac.gf_filter_setup_failure(self._filter, err)
##make the filter sticky - see \ref gf_filter_make_sticky
#\return
def make_sticky(self):
_libgpac.gf_filter_make_sticky(self._filter)
##prevent blocking on the filter - see \ref gf_filter_prevent_blocking
#\param enable if True, blocking prevention is enabled
#\return
def prevent_blocking(self, enable):
_libgpac.gf_filter_prevent_blocking(self._filter, enable)
##block eos signaling on the filter - see \ref gf_filter_block_eos
#\param enable if True, eos blocking is enabled
#\return
def block_eos(self, enable):
_libgpac.gf_filter_block_eos(self._filter, enable)
##set maximum number of extra pids accepted by this filter - see \ref gf_filter_set_max_extra_input_pids
#\param max_pids number of extra pids
#\return
def set_max_pids(self, max_pids):
_libgpac.gf_filter_set_max_extra_input_pids(self._filter, max_pids)
##set clock hint - see \ref gf_filter_hint_single_clock
#\param clock_us clock in microseconds
#\param media_time media time as Fraction64
#\return
def hint_clock(self, clock_us, media_time):
_libgpac.gf_filter_hint_single_clock(self._filter, clock_us, media_time)
##\cond private: until end, properties
@property
def block_enabled(self):
return _libgpac.gf_filter_block_enabled(self._filter)
@property
def output_buffer(self):
val = c_uint(0)
_libgpac.gf_filter_get_output_buffer_max(self._filter, byref(val), None)
return val.value
@property
def playout_buffer(self):
val = c_uint(0)
_libgpac.gf_filter_get_output_buffer_max(self._filter, None, byref(val))
return val.value
@property
def sinks_done(self):
return _libgpac.gf_filter_all_sinks_done(self._filter)
@property
def nb_evts_queued(self):
return _libgpac.gf_filter_get_num_events_queued(self._filter)
@property
def clock_hint_time(self):
val = c_ulonglong(0)
_libgpac.gf_filter_get_clock_hint(self._filter, byref(val), None)
return val.value
@property
def clock_hint_mediatime(self):
val = Fraction64()
_libgpac.gf_filter_get_clock_hint(self._filter, None, byref(val))
return val
@property
def connections_pending(self):
return _libgpac.gf_filter_connections_pending(self._filter)
##\endcond
#todo
#add_source, add_destination, add_filter: not needed, acces to filter session
#
# filter PID bindings
#
## \cond private
_libgpac.gf_filter_pid_copy_properties.argtypes = [_gf_filter_pid, _gf_filter_pid]
_libgpac.gf_filter_pck_forward.argtypes = [_gf_filter_packet, _gf_filter_pid]
_libgpac.gf_filter_pid_set_property.argtypes = [_gf_filter_pid, c_uint, POINTER(PropertyValue)]
_libgpac.gf_filter_pid_set_property_dyn.argtypes = [_gf_filter_pid, c_char_p, POINTER(PropertyValue)]
_libgpac.gf_filter_pid_set_info.argtypes = [_gf_filter_pid, c_uint, POINTER(PropertyValue)]
_libgpac.gf_filter_pid_set_info_dyn.argtypes = [_gf_filter_pid, c_char_p, POINTER(PropertyValue)]
_libgpac.gf_filter_pid_clear_eos.argtypes = [_gf_filter_pid, gf_bool]
_libgpac.gf_filter_pid_check_caps.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_check_caps.restype = gf_bool
_libgpac.gf_filter_pid_discard_block.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_allow_direct_dispatch.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_reset_properties.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_get_clock_info.argtypes = [_gf_filter_pid, POINTER(c_longlong), POINTER(c_uint)]
_libgpac.gf_filter_pid_remove.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_is_filter_in_parents.argtypes = [_gf_filter_pid, _gf_filter]
_libgpac.gf_filter_pid_is_filter_in_parents.restype = gf_bool
_libgpac.gf_filter_pid_get_name.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_get_name.restype = c_char_p
_libgpac.gf_filter_pid_set_name.argtypes = [_gf_filter_pid, c_char_p]
_libgpac.gf_filter_pid_is_eos.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_is_eos.restype = gf_bool
_libgpac.gf_filter_pid_set_eos.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_has_seen_eos.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_has_seen_eos.restype = gf_bool
_libgpac.gf_filter_pid_eos_received.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_eos_received.restype = gf_bool
_libgpac.gf_filter_pid_would_block.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_would_block.restype = gf_bool
_libgpac.gf_filter_pid_set_loose_connect.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_is_sparse.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_is_sparse.restype = gf_bool
_libgpac.gf_filter_pid_set_framing_mode.argtypes = [_gf_filter_pid, gf_bool]
_libgpac.gf_filter_pid_get_max_buffer.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_get_max_buffer.restype = c_uint
_libgpac.gf_filter_pid_set_max_buffer.argtypes = [_gf_filter_pid, c_uint]
_libgpac.gf_filter_pid_query_buffer_duration.argtypes = [_gf_filter_pid, gf_bool]
_libgpac.gf_filter_pid_query_buffer_duration.restype = c_ulonglong
_libgpac.gf_filter_pid_first_packet_is_empty.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_first_packet_is_empty.restype = gf_bool
_libgpac.gf_filter_pid_get_first_packet_cts.argtypes = [_gf_filter_pid, POINTER(c_ulonglong)]
_libgpac.gf_filter_pid_get_first_packet_cts.restype = gf_bool
_libgpac.gf_filter_pid_get_packet_count.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_get_timescale.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_set_clock_mode.argtypes = [_gf_filter_pid, gf_bool]
_libgpac.gf_filter_pid_set_discard.argtypes = [_gf_filter_pid, gf_bool]
_libgpac.gf_filter_pid_require_source_id.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_recompute_dts.argtypes = [_gf_filter_pid, gf_bool]
_libgpac.gf_filter_pid_get_min_pck_duration.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_is_playing.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_is_playing.restype = gf_bool
_libgpac.gf_filter_pid_get_filter_name.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_get_filter_name.restype = c_char_p
_libgpac.gf_filter_pid_get_next_ts.argtypes = [_gf_filter_pid]
_libgpac.gf_filter_pid_get_next_ts.restype = c_longlong
_libgpac.gf_filter_pid_caps_query.argtypes = [_gf_filter_pid, c_uint]
_libgpac.gf_filter_pid_caps_query.restype = POINTER(PropertyValue)
_libgpac.gf_filter_pid_caps_query_str.argtypes = [_gf_filter_pid, c_char_p]
_libgpac.gf_filter_pid_caps_query_str.restype = POINTER(PropertyValue)
_libgpac.gf_filter_pid_negotiate_property.argtypes = [_gf_filter_pid, c_uint, POINTER(PropertyValue)]
_libgpac.gf_filter_pid_negotiate_property_dyn.argtypes = [_gf_filter_pid, c_char_p, POINTER(PropertyValue)]
_libgpac.gf_filter_pck_new_ref.argtypes = [_gf_filter_pid, c_uint, c_uint, _gf_filter_packet]
_libgpac.gf_filter_pck_new_ref.restype = _gf_filter_packet
_libgpac.gf_filter_pck_new_alloc.argtypes = [_gf_filter_pid, c_uint, POINTER(c_void_p)]
_libgpac.gf_filter_pck_new_alloc.restype = _gf_filter_packet
_libgpac.gf_filter_pck_new_clone.argtypes = [_gf_filter_pid, _gf_filter_packet, POINTER(c_void_p)]
_libgpac.gf_filter_pck_new_clone.restype = _gf_filter_packet
_libgpac.gf_filter_pck_new_copy.argtypes = [_gf_filter_pid, _gf_filter_packet, POINTER(c_void_p)]
_libgpac.gf_filter_pck_new_copy.restype = _gf_filter_packet
## \endcond private
## \cond private
#callback for packet destruction
_libgpac.gf_filter_pck_new_shared.argtypes = [_gf_filter_pid, POINTER(c_ubyte), c_uint, c_void_p]
_libgpac.gf_filter_pck_new_shared.restype = _gf_filter_packet
@CFUNCTYPE(c_int, _gf_filter, _gf_filter_pid, _gf_filter_packet)
def filter_cbk_release_packet(_f, _pid, _pck):
obj = _libgpac.gf_filter_get_rt_udta(_f)
_filter = cast(obj, py_object).value
pck_obj=None
obj = _libgpac.gf_filter_pid_get_udta(_pid)
if obj:
pid_obj = cast(obj, py_object).value
else:
raise Exception('unknown PID on packet destruction callback')
for pck in pid_obj.pck_refs:
if pck._pck==_pck:
pck_obj = pck
break
if not pck_obj:
raise Exception('unknown packet on packet destruction callback')
pid_obj.pck_refs.remove(pck_obj)
_filter.packet_release(pid_obj, pck_obj)
return 0
## \endcond private
## Object representing a PID of a custom filter
class FilterPid:
## \cond private
def __init__(self, _filter, pid, IsInput):
self._filter = _filter
self._pid = pid
self._cur_pck = None
self._input = IsInput
## \endcond
#hack for doxygen to generate member vars (not support for parsing @property)
if 0:
##name of the PID - see \ref gf_filter_pid_get_name and \ref gf_filter_pid_set_name
#\hideinitializer
self.name=0
##name of the parent filter, readonly - see \ref gf_filter_pid_get_filter_name
#\hideinitializer
self.filter_name=0
##end of stream property of PID - see \ref gf_filter_pid_is_eos and \ref gf_filter_pid_set_eos
#\hideinitializer
self.eos=0
##True if end of stream was seen in the chain but has not yet reached the filter, readonly - see \ref gf_filter_pid_has_seen_eos
#\hideinitializer
self.has_seen_eos=0
##True if end of stream was seen on the input PID but some packets are still to be processed, readonly - see \ref gf_filter_pid_eos_received
#\hideinitializer
self.eos_received=0
##True if PID would block, readonly - see \ref gf_filter_pid_would_block
#\hideinitializer
self.would_block=0
##True if PID is sparse, readonly - see \ref gf_filter_pid_is_sparse
#\hideinitializer
self.sparse=0
##maximum buffer of PID in microseconds - see \ref gf_filter_pid_get_max_buffer and \ref gf_filter_pid_set_max_buffer
#\hideinitializer
self.max_buffer=0
##buffer of PID in microseconds, readonly - see \ref gf_filter_pid_query_buffer_duration
#\hideinitializer
self.buffer=0
##True if buffer is full, readonly - see \ref gf_filter_pid_query_buffer_duration
#\hideinitializer
self.buffer_full=0
##total level of buffer, readonly - see \ref gf_filter_pid_query_buffer_duration
#\hideinitializer
self.buffer_total=0
##True if no pending packet, readonly - see \ref gf_filter_pid_first_packet_is_empty
#\hideinitializer
self.first_empty=0
##value of CTS of first pending packet, None if none, readonly - see \ref gf_filter_pid_get_first_packet_cts
#\hideinitializer
self.first_cts=0
##number of queued packets for input pid, readonly - see \ref gf_filter_pid_get_packet_count
#\hideinitializer
self.nb_pck_queued=0
##timescale of pid, readonly - see \ref gf_filter_pid_get_timescale
#\hideinitializer
self.timescale=0
##minimum packet duration (in timescale) of pid, readonly - see \ref gf_filter_pid_get_min_pck_duration
#\hideinitializer
self.min_pck_dur=0
##True if PID is playing, readonly - see \ref gf_filter_pid_is_playing
#\hideinitializer
self.playing=0
##Next estimated timestamp on pid, readonly - see \ref gf_filter_pid_get_next_ts
#\hideinitializer
self.next_ts=0
##send an event on the pid - see \ref gf_filter_pid_send_event
#\param evt FilterEvent to send
#\return
def send_event(self, evt):
evt.base.on_pid = self._pid
_libgpac.gf_filter_pid_send_event(self._pid, byref(evt))
##removes this output pid - see \ref gf_filter_pid_remove
#\return
def remove(self):
if self._input:
raise Exception('Cannot remove input pid')
return
_libgpac.gf_filter_pid_remove(self._pid)
##enumerates property on pid
#\param callback_obj callback object to use, must have a 'on_prop_enum' method defined taking two parameters, prop_name(string) and propval(property value)
#\return
def enum_props(self, callback_obj):
self._filter._pid_enum_props_ex(callback_obj, self._pid)
##get a PID property
#\param pname property name
#\return property value or None if not found
def get_prop(self, pname):
return self._filter._pid_prop_ex(pname, self._pid, False)
##get a PID info
#\param pname property name
#\return property value or None if not found
def get_info(self, pname):
return self._filter._pid_prop_ex(pname, self._pid, True)
##get first packet of input PID - see gf_filter_pid_get_packet
#\return FilterPacket or None if no packet available
def get_packet(self):
##\cond private
if self._cur_pck:
return self._cur_pck
pck = _libgpac.gf_filter_pid_get_packet(self._pid)
if pck:
self._cur_pck = FilterPacket(pck, True)
return self._cur_pck
##\endcond
##drops (removes) the first packet of input PID - see \ref gf_filter_pid_drop_packet
#\return
def drop_packet(self):
##\cond private
if self._cur_pck:
self._cur_pck = None
_libgpac.gf_filter_pid_drop_packet(self._pid)
##\endcond
##copy property of given PID to the current pid - see \ref gf_filter_pid_copy_properties
#\param ipid FilterPid to copy from
#\return
def copy_props(self, ipid):
if self._input:
raise Exception('Cannot copy properties on input PID')
return
if ipid==None:
return
err = _libgpac.gf_filter_pid_copy_properties(self._pid, ipid._pid)
if err<0:
raise Exception('Cannot copy properties: ' + e2s(err) )
##removes all properties of the current pid - see \ref gf_filter_pid_reset_properties
#\return
def reset_props(self):
if self._input:
raise Exception('Cannot reset properties on input PID')
return
_libgpac.gf_filter_pid_reset_properties(self._pid)
##forward a packet on the current pid - see \ref gf_filter_pck_forward
#\param ipck packet to forward
#\return
def forward(self, ipck):
if self._input:
raise Exception('Cannot copy properties on input PID')
return
if ipck==None:
return
_libgpac.gf_filter_pck_forward(ipck._pck, self._pid)
##set a property the current pid - see \ref gf_filter_pid_set_property and \ref gf_filter_pid_set_property_str
#\param pcode property type
#\param prop property value to set
#\param custom_type type of property if user-defined property. If not set and user-defined, property is a string
#\return
def set_prop(self, pcode, prop, custom_type=0):
if self._input:
raise Exception('Cannot set properties on input PID')
return
prop_4cc = pcode
prop_name = None
if isinstance(pcode, str):
_pname = pcode.encode('utf-8')
prop_4cc = _libgpac.gf_props_get_id(_pname)
if prop_4cc==0:
prop_name = _pname
if prop==None:
if prop_4cc:
_libgpac.gf_filter_pid_set_property(self._pid, prop_4cc, None)
else:
_libgpac.gf_filter_pid_set_property_dyn(self._pid, prop_name, None)
return
prop_val = _make_prop(prop_4cc, pcode, prop, custom_type)
if prop_4cc:
_libgpac.gf_filter_pid_set_property(self._pid, prop_4cc, byref(prop_val))
else:
_libgpac.gf_filter_pid_set_property_dyn(self._pid, prop_name, byref(prop_val))
##set a info property the current pid - see \ref gf_filter_pid_set_info and \ref gf_filter_pid_set_info_str
#\param pcode property type
#\param prop property value to set
#\param custom_type type of property if user-defined property. If not set and user-defined, property is a string
#\return
def set_info(self, pcode, prop, custom_type=0):
if self._input:
raise Exception('Cannot set info on input PID')
return
prop_4cc = pcode
prop_name = None
if isinstance(pcode, str):
_pname = pcode.encode('utf-8')
prop_4cc = _libgpac.gf_props_get_id(_pname)
if prop_4cc==0:
prop_name = _pname
if prop==None:
if prop_4cc:
_libgpac.gf_filter_pid_set_info(self._pid, prop_4cc, None)
else:
_libgpac.gf_filter_pid_set_info_dyn(self._pid, prop_name, None)
return
prop_val = _make_prop(prop_4cc, pcode, prop, custom_type)
if prop_4cc:
_libgpac.gf_filter_pid_set_info(self._pid, prop_4cc, byref(prop_val))
else:
_libgpac.gf_filter_pid_set_info_dyn(self._pid, prop_name, byref(prop_val))
##clears EOS on the current PID - see \ref gf_filter_pid_clear_eos
#\param all_pids if True, clears eos on all input pids
#\return
def clear_eos(self, all_pids):
_libgpac.gf_filter_pid_clear_eos(self._pid, all_pids)
##check PID properties match capability of filter - see \ref gf_filter_pid_check_caps
#\return
def check_caps(self):
return _libgpac.gf_filter_pid_check_caps(self._pid)
##discard blocking mode on PID - see \ref gf_filter_pid_discard_block
#\return
def discard_block(self):
_libgpac.gf_filter_pid_discard_block(self._pid)
##allow direct dispatch of output to destinations - see \ref gf_filter_pid_allow_direct_dispatch
#\return
def allow_direct_dispatch(self):
_libgpac.gf_filter_pid_allow_direct_dispatch(self._pid)
##get current clock type info - see \ref gf_filter_pid_get_clock_info
#\return clock type (int)
def get_clock_type(self):
return _libgpac.gf_filter_pid_get_clock_info(self._pid, None, None)
##get current clock time stamp - see \ref gf_filter_pid_get_clock_info
#\return clock timestamp (Fraction64)
def get_clock_timestamp(self):
timestamp = c_longlong(0)
timescale = c_uint(0)
_libgpac.gf_filter_pid_get_clock_info(self._pid, byref(timestamp), byref(timescale))
v = Fraction64()
v.value.num = timestamp.value
v.value.den = timescale.value
return v
##check if a filter is in the parent filter chain of the pid - see \ref gf_filter_pid_is_filter_in_parents
#\param _filter Filter to check
#\return True or False
def is_filter_in_parents(self, _filter):
return _libgpac.gf_filter_pid_is_filter_in_parents(self._pid, _filter._f)
##get buffer occupancy - see \ref gf_filter_pid_get_buffer_occupancy
#\return BufferOccupancy object
def get_buffer_occupancy(self):
max_units = c_uint(0)
nb_pck = c_uint(0)
max_dur = c_uint(0)
dur = c_uint(0)
in_final_flush = _libgpac.gf_filter_pid_get_buffer_occupancy(self._pid, byref(max_units), byref(nb_pck), byref(max_dur), byref(dur) )
in_final_flush = not in_final_flush
return BufferOccupancy(max_units, nb_pck, max_dur, dur, in_final_flush)
##sets loose connect mode - see \ref gf_filter_pid_set_loose_connect
#\return
def loose_connect(self):
_libgpac.gf_filter_pid_set_loose_connect(self._pid)
##sets framing mode - see \ref gf_filter_pid_set_framing_mode
#\param framed if True, complete frames only will be delivered on the pid
#\return
def set_framing(self, framed):
_libgpac.gf_filter_pid_set_framing_mode(self._pid, framed)
##sets clock mode - see \ref gf_filter_pid_set_clock_mode
#\param cmode clock mode operation of filter
#\return
def set_clock_mode(self, cmode):
_libgpac.gf_filter_pid_set_clock_mode(self._pid, cmode)
##sets discard mode - see \ref gf_filter_pid_set_discard
#\param do_discard if True, discard is on
#\return
def set_discard(self, do_discard):
_libgpac.gf_filter_pid_set_discard(self._pid, do_discard)
##enforces sourceID to be present for output pids of this filter - see \ref gf_filter_pid_require_source_id
#\return
def require_source_id(self):
err = _libgpac.gf_filter_pid_require_source_id(self._pid)
if err<0:
raise Exception('Failed to require sourceID for pid: ' + e2s(err))
return
##sets DTS recomputing mode - see \ref gf_filter_pid_recompute_dts
#\param do_compute if True, DTS are recomputed
#\return
def recompute_dts(self, do_compute):
_libgpac.gf_filter_pid_recompute_dts(self._pid, do_compute)
##queries a capability property on output PID - see \ref gf_filter_pid_caps_query and \ref gf_filter_pid_caps_query_str
#\param pcode property to check
#\return property value if found, None otherwise
def query_cap(self, pcode):
if self._input:
raise Exception('Cannot query caps on input PID')
return
prop_4cc = pcode
prop_name = None
if isinstance(pcode, str):
_pname = pcode.encode('utf-8')
prop_4cc = _libgpac.gf_props_get_id(_pname)
if prop_4cc==0:
prop_name = _pname
if prop_4cc:
prop_val = _libgpac.gf_filter_pid_caps_query(self._pid, prop_4cc)
else:
prop_val = _libgpac.gf_filter_pid_caps_query_str(self._pid, _pname)
if prop_val:
return _prop_to_python(prop_name, prop_val.contents)
return None
##negotiates a capability property on input PID - see \ref gf_filter_pid_negotiate_property and \ref gf_filter_pid_negotiate_property_dyn
#\param pcode property to negotiate
#\param prop property to negotiate
#\param custom_type type of property if user-defined property. If not set and user-defined, property is a string
#\return
def negotiate_cap(self, pcode, prop, custom_type=0):
if not self._input:
raise Exception('Cannot negotiate caps on output PID')
return
prop_4cc = pcode
prop_name = None
if isinstance(pcode, str):
_pname = pcode.encode('utf-8')
prop_4cc = _libgpac.gf_props_get_id(_pname)
if prop_4cc==0:
prop_name = _pname
prop_val = _make_prop(prop_4cc, pcode, prop, custom_type)
if prop_4cc:
_libgpac.gf_filter_pid_negotiate_property(self._pid, prop_4cc, byref(prop_val))
else:
_libgpac.gf_filter_pid_negotiate_property_dyn(self._pid, prop_name, byref(prop_val))
##resolves a template string - see \ref gf_filter_pid_resolve_file_template
#\param template the template string
#\param file_idx the file index
#\param suffix the file suffix
#\return the resolved template string
def resolve_template(self, template, file_idx=0, suffix=None):
res = create_string_buffer(2000)
err = _libgpac.gf_filter_pid_resolve_file_template(self._pid, res, template.encode('utf-8'), file_idx, suffix)
if err<0:
raise Exception('Cannot resolve file template ' + template + ': ' + e2s(err))
return res.raw.decode('utf-8')
##creates a new packet referring to an existing packet - see \ref gf_filter_pck_new_ref
#\param ipck the input (referenced) packet
#\param size the data size of the new packet
#\param offset the offset in the original data
#\return the new FilterPacket or None if failure
def new_pck_ref(self, ipck, size=0, offset=0):
if ipck==None:
raise Exception('NULL input packet in new_pck_ref !')
_pck = _libgpac.gf_filter_pck_new_ref(self._pid, offset, size, ipck._pck)
if _pck:
return FilterPacket(_pck, False)
return None
##creates a new packet of the given size, allocating memory in libgpac - see \ref gf_filter_pck_new_alloc
#\param size the data size of the new packet
#\return the new FilterPacket or None if failure
def new_pck(self, size=0):
_pck = _libgpac.gf_filter_pck_new_alloc(self._pid, size, None)
if _pck:
pck = FilterPacket(_pck, False)
pck._readonly = False
return pck
return None
##creates a new packet sharing memory of the filter - see \ref gf_filter_pck_new_shared
#The filter object must have a `packet_release` method with arguments [ FilterPid, FilterPacket ]
#\param data the data to use. If NumPy is detected, accept a NP Array. Otherwise data is type casted into POINTER(c_ubyte)
#\return the new FilterPacket or None if failure
def new_pck_shared(self, data):
data_p = None
data_len = 0
readonly=False
if not hasattr(self._filter, 'packet_release'):
raise Exception('Filter ' + self._filter.name + ' has no packet_release callback function defined')
if numpy_support and isinstance(data, np.ndarray):
data_p = data.ctypes.data_as(POINTER(c_ubyte))
data_len = data.size * data.itemsize
readonly = not data.flags.writeable
else:
data_p = cast(data, POINTER(c_ubyte) )
data_len = len(data)
if not data_p:
raise Exception('Unknown data reference class ' + data.__class__.__name__)
_pck = _libgpac.gf_filter_pck_new_shared(self._pid, data_p, data_len, filter_cbk_release_packet)
if _pck:
pck = FilterPacket(_pck, False)
pck._readonly = readonly
self.pck_refs.append(pck)
return pck
return None
##creates a new packet copying a source packet - see \ref gf_filter_pck_new_copy
#\param ipck the FilterPacket to copy
#\return the new FilterPacket or None if failure
def new_pck_copy(self, ipck):
if ipck==None:
raise Exception('NULL input packet in new_pck_copy !')
_pck = _libgpac.gf_filter_pck_new_copy(self._pid, ipck._pck, None)
if _pck:
pck = FilterPacket(_pck, False)
pck._readonly = False
return pck
return None
##creates a new packet cloning a source packet - see \ref gf_filter_pck_new_clone
#\param ipck the FilterPacket to clone
#\return the new FilterPacket or None if failure
def new_pck_clone(self, ipck):
if ipck==None:
raise Exception('NULL input packet in new_pck_clone !')
_pck = _libgpac.gf_filter_pck_new_clone(self._pid, ipck._pck, None)
if _pck:
pck = FilterPacket(_pck, False)
pck._readonly = False
return pck
return None
##\cond private: until end, properties
##name of the PID - see \ref gf_filter_pid_get_name and \ref gf_filter_pid_set_name
#\return
@property
def name(self):
return _libgpac.gf_filter_pid_get_name(self._pid).decode('utf-8')
##\cond private
@name.setter
def name(self, val):
_libgpac.gf_filter_pid_set_name(self._pid, val.encode('utf-8'))
##\endcond
##name of the parent filter - see \ref gf_filter_pid_get_filter_name
#\return
@property
def filter_name(self):
return _libgpac.gf_filter_pid_get_filter_name(self._pid).decode('utf-8')
##end of stream property of PID - see \ref gf_filter_pid_is_eos and \ref gf_filter_pid_set_eos
#\return
@property
def eos(self):
return _libgpac.gf_filter_pid_is_eos(self._pid)
##\cond private
@eos.setter
def eos(self, Value):
_libgpac.gf_filter_pid_set_eos(self._pid)
##\endcond private
##True if end of stream was seen in the chain but not yet reached by the filter - see \ref gf_filter_pid_has_seen_eos
#\return
@property
def has_seen_eos(self):
return _libgpac.gf_filter_pid_has_seen_eos(self._pid)
##True if end of stream was seen on pid but some packets are still pending - see \ref gf_filter_pid_eos_received
#\return
@property
def eos_received(self):
return _libgpac.gf_filter_pid_eos_received(self._pid)
##True if PID would block - see \ref gf_filter_pid_would_block
#\return
@property
def would_block(self):
return _libgpac.gf_filter_pid_would_block(self._pid)
##True if PID is sparse - see \ref gf_filter_pid_is_sparse
#\return
@property
def sparse(self):
return _libgpac.gf_filter_pid_is_sparse(self._pid)
##maximum buffer of PID in microseconds - see \ref gf_filter_pid_get_max_buffer and \ref gf_filter_pid_set_max_buffer
#\return
@property
def max_buffer(self):
return _libgpac.gf_filter_pid_get_max_buffer(self._pid)
##\cond private
@max_buffer.setter
def max_buffer(self, value):
return _libgpac.gf_filter_pid_set_max_buffer(self._pid, value)
##\endcond
##buffer of PID in microseconds - see \ref gf_filter_pid_query_buffer_duration
#\return
@property
def buffer(self):
return _libgpac.gf_filter_pid_query_buffer_duration(self._pid, False)
##True if buffer is full - see \ref gf_filter_pid_query_buffer_duration
#\return
@property
def buffer_full(self):
dur = _libgpac.gf_filter_pid_query_buffer_duration(self._pid, True)
if dur:
return True
else:
return False
##total buffer level - see \ref gf_filter_pid_query_buffer_duration
#\return
@property
def buffer_total(self):
return _libgpac.gf_filter_pid_query_buffer_duration(self._pid, True)
##True if no pending packet - see \ref gf_filter_pid_first_packet_is_empty
#\return
@property
def first_empty(self):
return _libgpac.gf_filter_pid_first_packet_is_empty(self._pid)
##value of CTS of first pending packet, None if none - see \ref gf_filter_pid_get_first_packet_cts
#\return
@property
def first_cts(self):
cts = c_ulonglong(0)
res = _libgpac.gf_filter_pid_get_first_packet_cts(self._pid, byref(cts))
if not res:
return None
return cts.value
##number of queued packets for input pid - see \ref gf_filter_pid_get_packet_count
#\return
@property
def nb_pck_queued(self):
return _libgpac.gf_filter_pid_get_packet_count(self._pid)
##timescale of pid - see \ref gf_filter_pid_get_timescale
#\return
@property
def timescale(self):
return _libgpac.gf_filter_pid_get_timescale(self._pid)
##minimum packet duration (in timescale) of pid - see \ref gf_filter_pid_get_min_pck_duration
#\return
@property
def min_pck_dur(self):
return _libgpac.gf_filter_pid_get_min_pck_duration(self._pid)
##True if PID is playing - see \ref gf_filter_pid_is_playing
#\return
@property
def playing(self):
return _libgpac.gf_filter_pid_is_playing(self._pid)
#\return
@property
def next_ts(self):
return _libgpac.gf_filter_pid_get_next_ts(self._pid)
##\endcond
#vars:
#src_name, args, src_args, src_url, dst_url: probably not needed since the entire session is exposed to the custom filter
#
# filter packet bindings
#
##\cond private
#getters
_libgpac.gf_filter_pck_get_dts.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_dts.restype = c_ulonglong
_libgpac.gf_filter_pck_set_dts.argtypes = [_gf_filter_packet, c_ulonglong]
_libgpac.gf_filter_pck_get_cts.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_cts.restype = c_ulonglong
_libgpac.gf_filter_pck_set_cts.argtypes = [_gf_filter_packet, c_ulonglong]
_libgpac.gf_filter_pck_get_sap.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_sap.restype = c_uint
_libgpac.gf_filter_pck_set_sap.argtypes = [_gf_filter_packet, c_uint]
_libgpac.gf_filter_pck_get_duration.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_duration.restype = c_uint
_libgpac.gf_filter_pck_set_duration.argtypes = [_gf_filter_packet, c_uint]
_libgpac.gf_filter_pck_get_data.argtypes = [_gf_filter_packet, POINTER(c_uint)]
_libgpac.gf_filter_pck_get_data.restype = POINTER(c_ubyte)
_libgpac.gf_filter_pck_get_framing.argtypes = [_gf_filter_packet, POINTER(gf_bool), POINTER(gf_bool)]
_libgpac.gf_filter_pck_set_framing.argtypes = [_gf_filter_packet, gf_bool, gf_bool]
_libgpac.gf_filter_pck_get_timescale.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_timescale.restype = c_uint
_libgpac.gf_filter_pck_get_interlaced.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_interlaced.restype = c_uint
_libgpac.gf_filter_pck_set_interlaced.argtypes = [_gf_filter_packet, c_uint]
_libgpac.gf_filter_pck_get_corrupted.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_corrupted.restype = gf_bool
_libgpac.gf_filter_pck_set_corrupted.argtypes = [_gf_filter_packet, gf_bool]
_libgpac.gf_filter_pck_get_seek_flag.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_seek_flag.restype = gf_bool
_libgpac.gf_filter_pck_set_seek_flag.argtypes = [_gf_filter_packet, gf_bool]
_libgpac.gf_filter_pck_get_byte_offset.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_byte_offset.restype = c_ulonglong
_libgpac.gf_filter_pck_set_byte_offset.argtypes = [_gf_filter_packet, c_ulonglong]
_libgpac.gf_filter_pck_get_roll_info.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_roll_info.restype = c_short
_libgpac.gf_filter_pck_set_roll_info.argtypes = [_gf_filter_packet, c_short]
_libgpac.gf_filter_pck_get_crypt_flags.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_crypt_flags.restype = c_uint
_libgpac.gf_filter_pck_set_crypt_flags.argtypes = [_gf_filter_packet, c_uint]
_libgpac.gf_filter_pck_get_clock_type.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_clock_type.restype = c_uint
_libgpac.gf_filter_pck_set_clock_type.argtypes = [_gf_filter_packet, c_uint]
_libgpac.gf_filter_pck_get_carousel_version.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_carousel_version.restype = c_uint
_libgpac.gf_filter_pck_set_carousel_version.argtypes = [_gf_filter_packet, c_uint]
_libgpac.gf_filter_pck_get_seq_num.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_seq_num.restype = c_uint
_libgpac.gf_filter_pck_set_seq_num.argtypes = [_gf_filter_packet, c_uint]
_libgpac.gf_filter_pck_get_dependency_flags.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_dependency_flags.restype = c_uint
_libgpac.gf_filter_pck_set_dependency_flags.argtypes = [_gf_filter_packet, c_uint]
class FrameInterface(Structure):
_fields_ = [
("get_plane", c_void_p),
("get_gl_texture", c_void_p),
("flags", c_uint),
("user_data", c_void_p)
]
get_gl_tex_fun = CFUNCTYPE(c_int, POINTER(FrameInterface), c_uint, POINTER(c_uint), POINTER(c_uint), POINTER(c_void_p))
_libgpac.gf_filter_pck_get_frame_interface.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_get_frame_interface.restype = POINTER(FrameInterface)
_libgpac.gf_filter_pck_is_blocking_ref.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_is_blocking_ref.restype = gf_bool
_libgpac.gf_filter_pck_has_properties.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_has_properties.restype = gf_bool
_libgpac.gf_filter_pck_enum_properties.argtypes = [_gf_filter_packet, POINTER(c_uint), POINTER(c_uint), POINTER(c_char_p)]
_libgpac.gf_filter_pck_enum_properties.restype = POINTER(PropertyValue)
_libgpac.gf_filter_pck_get_property.argtypes = [_gf_filter_packet, c_uint]
_libgpac.gf_filter_pck_get_property.restype = POINTER(PropertyValue)
_libgpac.gf_filter_pck_get_property_str.argtypes = [_gf_filter_packet, c_char_p]
_libgpac.gf_filter_pck_get_property_str.restype = POINTER(PropertyValue)
_libgpac.gf_filter_pck_ref_ex.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_ref_ex.restype = _gf_filter_packet
_libgpac.gf_filter_pck_unref.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_discard.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_set_readonly.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_send.argtypes = [_gf_filter_packet]
_libgpac.gf_filter_pck_merge_properties.argtypes = [_gf_filter_packet, _gf_filter_packet]
_libgpac.gf_filter_pck_set_property.argtypes = [_gf_filter_packet, c_uint, POINTER(PropertyValue)]
_libgpac.gf_filter_pck_set_property_dyn.argtypes = [_gf_filter_packet, c_char_p, POINTER(PropertyValue)]
_libgpac.gf_filter_pck_truncate.argtypes = [_gf_filter_packet, c_uint]
_libgpac.gf_filter_pck_dangling_copy.argtypes = [_gf_filter_packet, _gf_filter_packet]
_libgpac.gf_filter_pck_dangling_copy.restype = _gf_filter_packet
##\endcond private
## OpenGL texture info
class GLTextureInfo:
##\cond private
def __init__(self, gl_id, gl_format):
##\endcond
##OpenGL texture ID
#\hideinitializer
self.id = gl_id
##OpenGL texture format (e.g., GL_TEXTURE_2D)
#\hideinitializer
self.format = gl_format
##filter packet object
class FilterPacket:
##\cond private
def __init__(self, pck, is_src):
self._pck = pck
self._is_src = is_src
self._readonly = True
##\endcond private
#hack for doxygen to generate member vars (not support for parsing @property)
if 0:
##Decode Timestamp - see \ref gf_filter_pck_get_dts and \ref gf_filter_pck_set_dts
#\hideinitializer
self.dts=0
##Compose Timestamp - see \ref gf_filter_pck_get_cts and \ref gf_filter_pck_set_cts
#\hideinitializer
self.cts=0
##SAP type - see \ref gf_filter_pck_get_sap and \ref gf_filter_pck_set_sap
#\hideinitializer
self.sap=0
##Duration - see \ref gf_filter_pck_get_duration and \ref gf_filter_pck_set_duration
#\hideinitializer
self.dur=0
##Size of packet data, readonly
#\hideinitializer
self.size=0
##Packet data - see \ref gf_filter_pck_get_data
#If NumPy is available, the data is returned as a np Array, otherwise it is a POINTER(c_ubyte) object
#The property is readonly (cannot be set) but content can be modified for non-refs output packets
#\hideinitializer
self.data=0
##frame start - see \ref gf_filter_pck_get_framing and \ref gf_filter_pck_set_framing
#\hideinitializer
self.start=0
##frame end - see \ref gf_filter_pck_get_framing and \ref gf_filter_pck_set_framing
#\hideinitializer
self.end=0
##associated timescale, readonly - see \ref gf_filter_pck_get_timescale
#\hideinitializer
self.timescale=0
##Interlaced flags - see \ref gf_filter_pck_get_interlaced and \ref gf_filter_pck_set_interlaced
#\hideinitializer
self.interlaced=0
##Corrupted flag - see \ref gf_filter_pck_get_corrupted and \ref gf_filter_pck_set_corrupted
#\hideinitializer
self.corrupted=0
##Seek flag - see \ref gf_filter_pck_get_seek_flag and \ref gf_filter_pck_set_seek_flag
#\hideinitializer
self.seek=0
##Byte offset - see \ref gf_filter_pck_get_byte_offset and \ref gf_filter_pck_set_byte_offset
#\hideinitializer
self.byte_offset=0
##Roll info - see \ref gf_filter_pck_get_roll_info and \ref gf_filter_pck_set_roll_info
#\hideinitializer
self.roll=0
##Encryption flags - see \ref gf_filter_pck_get_crypt_flags and \ref gf_filter_pck_set_crypt_flags
#\hideinitializer
self.crypt=0
##Clock reference flag - see \ref gf_filter_pck_get_clock_type and \ref gf_filter_pck_set_clock_type
#\hideinitializer
self.clock=0
##Carousel version - see \ref gf_filter_pck_get_carousel_version and \ref gf_filter_pck_set_carousel_version
#\hideinitializer
self.carousel=0
##Sequence number - see \ref gf_filter_pck_get_seq_num and \ref gf_filter_pck_set_seq_num
#\hideinitializer
self.seqnum=0
##Dependency flags - see \ref gf_filter_pck_get_dependency_flags and \ref gf_filter_pck_set_dependency_flags
#\hideinitializer
self.deps=0
##true if packet holds a GF_FrameInterface object and not a data packet, readonly
#\hideinitializer
self.frame_ifce=0
##true if packet holds a GF_FrameInterface object providing OpenGL only access and not a data packet, readonly
#\hideinitializer
self.frame_ifce_gl=0
##Custom properties present, readonly - see \ref gf_filter_pck_has_properties
#\hideinitializer
self.has_properties=0
##true if packet is a blocking reference, readonly - see \ref gf_filter_pck_is_blocking_ref
#\hideinitializer
self.blocking_ref=0
##enumerate an packet properties
#\param callback_obj callback object to use, must have a 'on_prop_enum' method defined taking two parameters, prop_name(string) and propval
#\return
def enum_props(self, callback_obj):
if not hasattr(callback_obj, 'on_prop_enum'):
return
pidx=c_uint(0)
while True:
_p4cc=c_uint(0)
_pname=c_char_p(0)
prop = _libgpac.gf_filter_pck_enum_properties(self._pck, byref(pidx), byref(_p4cc), byref(_pname))
if prop:
p4cc=_p4cc.value
pname=None
if _pname:
pname=_pname.value.decode('utf-8')
else:
pname=_libgpac.gf_props_4cc_get_name(p4cc).decode('utf-8')
pval = _prop_to_python(pname, prop.contents)
callback_obj.on_prop_enum(pname, pval)
else:
break
##get a packet property - see \ref gf_filter_pck_get_property and \ref gf_filter_pck_get_property_str
#\param prop_name name of property to get
#\return property value, or None if not found
def get_prop(self, prop_name):
_name = prop_name.encode('utf-8')
p4cc = _libgpac.gf_props_get_id(_name)
if p4cc:
prop = _libgpac.gf_filter_pck_get_property(self._pck, p4cc)
else:
prop = _libgpac.gf_filter_pck_get_property_str(self._pck, _name)
if prop:
return _prop_to_python(prop_name, prop.contents)
return None
##increase packet reference count - see \ref gf_filter_pck_ref_ex
#\return
def ref(self):
##\cond private
self._pck = _libgpac.gf_filter_pck_ref_ex(self._pck)
##\endcond private
##decrease packet reference count - see \ref gf_filter_pck_unref
#\return
def unref(self):
_libgpac.gf_filter_pck_unref(self._pck)
##discard an output packet instead of sending it - see \ref gf_filter_pck_discard
#\return
def discard(self):
##\cond private
if self._is_src:
raise Exception('Cannot discard on source packet')
_libgpac.gf_filter_pck_discard(self._pck)
self._pck = None
##\endcond private
##creates a new packet cloning a source packet - see \ref gf_filter_pck_dangling_copy.
#The resulting packet is read/write mode and may have its own memory allocated.
#This is typically used by sink filters wishing to access underling GPU data of a packet using frame interface.
#the resulting packet can be explicitly discarded using \ref python.libgpac.libgpac.FilterPacket.discard "discard()", otherwise will be garbage collected.
#\param cached_pck if set, will be reuse for creation of new packet. This can greatly reduce memory allocations
#\return the new FilterPacket or None if failure or None if failure ( if grabbing the frame into a local copy failed)
def clone(self, cached_pck=None):
if cached_pck:
_pck = _libgpac.gf_filter_pck_dangling_copy(self._pck, cached_pck._pck)
else:
_pck = _libgpac.gf_filter_pck_dangling_copy(self._pck, None)
if _pck:
pck = FilterPacket(_pck, False)
pck._readonly = False
return pck
return None
##mark an output packet as readonly - see \ref gf_filter_pck_set_readonly
#\return
def readonly(self):
if self._is_src:
raise Exception('Cannot set readonly on source packet')
_libgpac.gf_filter_pck_set_readonly(self._pck)
##send the packet - see \ref gf_filter_pck_send
#\return
def send(self):
if self._is_src:
raise Exception('Cannot send on source packet')
_libgpac.gf_filter_pck_send(self._pck)
##copy properties of source packet in this packet - see \ref gf_filter_pck_merge_properties
#\param ipck source packet
#\return
def copy_props(self, ipck):
if self._is_src:
raise Exception('Cannot copy properties on source packet')
if ipck==None:
return
err = _libgpac.gf_filter_pck_merge_properties(ipck._pck, self._pck)
if err<0:
raise Exception('Cannot copy properties: ' + e2s(err) )
##set property in this packet - see \ref gf_filter_pck_set_property and \ref gf_filter_pck_set_property_str
#\param pcode name of property
#\param prop property value
#\param custom_type type of property if user-defined property. If not set and user-defined, property is a string
#\return
def set_prop(self, pcode, prop, custom_type=0):
if self._is_src:
raise Exception('Cannot copy properties on source packet')
prop_4cc = pcode
prop_name = None
if isinstance(pcode, str):
_pname = pcode.encode('utf-8')
prop_4cc = _libgpac.gf_props_get_id(_pname)
if prop_4cc==0:
prop_name = _pname
if prop==None:
if prop_4cc:
_libgpac.gf_filter_pck_set_property(self._pck, prop_4cc, None)
else:
_libgpac.gf_filter_pck_set_property_dyn(self._pck, prop_name, None)
return
prop_val = _make_prop(prop_4cc, pcode, prop, custom_type)
if prop_4cc:
_libgpac.gf_filter_pck_set_property(self._pck, prop_4cc, byref(prop_val))
else:
_libgpac.gf_filter_pck_set_property_dyn(self._pck, prop_name, byref(prop_val))
##truncates an output packet to the given size - see \ref gf_filter_pck_truncate
#\param size new size of packet
#\return
def truncate(self, size):
if self._is_src:
raise Exception('Cannot truncate an source packet')
_libgpac.gf_filter_pck_truncate(self._pck, size)
##return OpenGL texture info for a given color plane of a frame interface packet
#\param idx index of texture to fetch. The number of texture depends on the underlying pixel format
#\return GLTextureInfo object or None if error
def get_gl_texture(self, idx):
p = _libgpac.gf_filter_pck_get_frame_interface(self._pck)
if not p:
return None
if not p.contents.get_gl_texture:
return None
get_tex = get_gl_tex_fun(p.contents.get_gl_texture)
gl_format=c_uint(0)
gl_tex_id=c_uint(0)
ret = get_tex(p, idx, byref(gl_format), byref(gl_tex_id), None)
if ret:
return None
obj = GLTextureInfo(gl_tex_id.value, gl_format.value)
return obj
##\cond private: until end, all packet properties
@property
def blocking_ref(self):
return _libgpac.gf_filter_pck_is_blocking_ref(self._pck)
@property
def dts(self):
return _libgpac.gf_filter_pck_get_dts(self._pck)
@dts.setter
def dts(self, val):
if self._is_src:
raise Exception('Cannot set DTS on source packet')
_libgpac.gf_filter_pck_set_dts(self._pck, val)
@property
def cts(self):
return _libgpac.gf_filter_pck_get_cts(self._pck)
@cts.setter
def cts(self, val):
if self._is_src:
raise Exception('Cannot set CTS on source packet')
return _libgpac.gf_filter_pck_set_cts(self._pck, val)
@property
def sap(self):
return _libgpac.gf_filter_pck_get_sap(self._pck)
@sap.setter
def sap(self, val):
if self._is_src:
raise Exception('Cannot set SAP on source packet')
return _libgpac.gf_filter_pck_set_sap(self._pck, val)
@property
def dur(self):
return _libgpac.gf_filter_pck_get_duration(self._pck)
@dur.setter
def dur(self, val):
if self._is_src:
raise Exception('Cannot set duration on source packet')
return _libgpac.gf_filter_pck_set_duration(self._pck, val)
@property
def size(self):
size = c_uint(0)
_libgpac.gf_filter_pck_get_data(self._pck, byref(size))
return size.value
@property
def data(self):
size = c_uint(0)
data = _libgpac.gf_filter_pck_get_data(self._pck, byref(size))
if not data:
return None
if numpy_support:
data = np.ctypeslib.as_array(data, (size.value,))
if self._readonly:
data.flags.writeable=False
return data
@property
def start(self):
start = gf_bool(False)
end = gf_bool(False)
_libgpac.gf_filter_pck_get_framing(self._pck, byref(start), byref(end))
return start.value
@start.setter
def start(self, value):
if self._is_src:
raise Exception('Cannot set framing on source packet')
start = gf_bool(False)
end = gf_bool(False)
_libgpac.gf_filter_pck_get_framing(self._pck, byref(start), byref(end))
_libgpac.gf_filter_pck_set_framing(self._pck, value, end.value)
@property
def end(self):
start = gf_bool(False)
end = gf_bool(False)
_libgpac.gf_filter_pck_get_framing(self._pck, byref(start), byref(end))
return end.value
@end.setter
def end(self, value):
if self._is_src:
raise Exception('Cannot set framing on source packet')
start = gf_bool(False)
end = gf_bool(False)
_libgpac.gf_filter_pck_get_framing(self._pck, byref(start), byref(end))
_libgpac.gf_filter_pck_set_framing(self._pck, start.value, value)
@property
def timescale(self):
return _libgpac.gf_filter_pck_get_timescale(self._pck)
@property
def interlaced(self):
return _libgpac.gf_filter_pck_get_interlaced(self._pck)
@interlaced.setter
def interlaced(self, value):
if self._is_src:
raise Exception('Cannot set interlaced on source packet')
return _libgpac.gf_filter_pck_set_interlaced(self._pck, value)
@property
def corrupted(self):
return _libgpac.gf_filter_pck_get_corrupted(self._pck)
@corrupted.setter
def corrupted(self, value):
if self._is_src:
raise Exception('Cannot set corrupted on source packet')
return _libgpac.gf_filter_pck_set_corrupted(self._pck, value)
@property
def seek(self):
return _libgpac.gf_filter_pck_get_seek_flag(self._pck)
@seek.setter
def seek(self, value):
if self._is_src:
raise Exception('Cannot set seek on source packet')
return _libgpac.gf_filter_pck_set_seek_flag(self._pck, value)
@property
def byte_offset(self):
return _libgpac.gf_filter_pck_get_byte_offset(self._pck)
@byte_offset.setter
def byte_offset(self, value):
if self._is_src:
raise Exception('Cannot set byte offset on source packet')
return _libgpac.gf_filter_pck_set_byte_offset(self._pck, value)
@property
def roll(self):
return _libgpac.gf_filter_pck_get_roll_info(self._pck)
@roll.setter
def roll(self, value):
if self._is_src:
raise Exception('Cannot set roll on source packet')
return _libgpac.gf_filter_pck_set_roll_info(self._pck, value)
@property
def crypt(self):
return _libgpac.gf_filter_pck_get_crypt_flags(self._pck)
@crypt.setter
def crypt(self, value):
if self._is_src:
raise Exception('Cannot set crypt on source packet')
return _libgpac.gf_filter_pck_set_crypt_flags(self._pck, value)
@property
def clock(self):
return _libgpac.gf_filter_pck_get_clock_type(self._pck)
@clock.setter
def clock(self, value):
if self._is_src:
raise Exception('Cannot set clock on source packet')
return _libgpac.gf_filter_pck_set_clock_type(self._pck, value)
@property
def carousel(self):
return _libgpac.gf_filter_pck_get_carousel_version(self._pck)
@carousel.setter
def carousel(self, value):
if self._is_src:
raise Exception('Cannot set carousel on source packet')
return _libgpac.gf_filter_pck_set_carousel_version(self._pck, value)
@property
def seqnum(self):
return _libgpac.gf_filter_pck_get_seq_num(self._pck)
@seqnum.setter
def seqnum(self, value):
if self._is_src:
raise Exception('Cannot set segnum on source packet')
return _libgpac.gf_filter_pck_set_seq_num(self._pck, value)
@property
def deps(self):
return _libgpac.gf_filter_pck_get_dependency_flags(self._pck)
@deps.setter
def deps(self, value):
if self._is_src:
raise Exception('Cannot set deps on source packet')
return _libgpac.gf_filter_pck_set_dependency_flags(self._pck, value)
@property
def has_properties(self):
if _libgpac.gf_filter_pck_has_properties(self._pck):
return True
return False
@property
def frame_ifce(self):
p = _libgpac.gf_filter_pck_get_frame_interface(self._pck)
if p:
return True
return False
@property
def frame_ifce_gl(self):
p = _libgpac.gf_filter_pck_get_frame_interface(self._pck)
if not p:
return False
if p.contents == 0:
return False
if p.contents.get_gl_texture == None:
return False
return True
##\endcond private
#todo
#append ?
## @}
##\cond private
_gf_fileio = c_void_p
_libgpac.gf_fileio_new.argtypes = [c_char_p, py_object, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p]
_libgpac.gf_fileio_new.restype = _gf_fileio
_libgpac.gf_fileio_url.argtypes = [_gf_fileio]
_libgpac.gf_fileio_url.restype = c_char_p
_libgpac.gf_fileio_resource_url.argtypes = [_gf_fileio]
_libgpac.gf_fileio_resource_url.restype = c_char_p
_libgpac.gf_fileio_translate_url.argtypes = [c_char_p]
_libgpac.gf_fileio_translate_url.restype = c_char_p
_libgpac.gf_fileio_get_udta.argtypes = [_gf_fileio]
_libgpac.gf_fileio_get_udta.restype = c_void_p
_libgpac.gf_fileio_del.argtypes = [_gf_fileio]
_libgpac.gf_fileio_tag_main_thread.argtypes = [_gf_fileio]
#wraps gf_url_concatenate to be able to free the value
#we declare a void * return value
_libgpac.gf_url_concatenate.argtypes = [c_char_p, c_char_p]
_libgpac.gf_url_concatenate.restype = c_void_p
_libgpac.gf_url_free.argtypes = [c_void_p]
def url_concatenate(_parent, _url):
#this is c_void_p
_path = _libgpac.gf_url_concatenate(_parent, _url)
if _path==None:
raise Exception('Failed to concatenate path, out of memory')
#this is a string
path = cast(_path, c_char_p).value.decode('utf-8')
#we still have the pointer, we can free it
_libgpac.gf_url_free(_path)
return path
#fio open callback
@CFUNCTYPE(_gf_fileio, _gf_fileio, c_char_p, c_char_p, POINTER(c_int))
def fileio_cbk_open(_fio_ref, _url, _mode, error):
fio_ref = cast(_libgpac.gf_fileio_get_udta(_fio_ref), py_object).value
mode = _mode.decode('utf-8')
error.contents.value=GF_OK
if mode=="url":
path = url_concatenate(_libgpac.gf_fileio_resource_url(_fio_ref), _url)
new_gfio = FileIO(path, fio_ref)
fio_ref.factory.pending_urls.append(new_gfio)
#prevent garbage collection
fio_ref.factory.gc_exclude.append(new_gfio)
fio_ref.factory.all_refs+=1
return new_gfio._gf_fio
if mode=="ref":
fio_ref.nb_refs+=1
fio_ref.factory.all_refs+=1
return _fio_ref
if mode=="probe":
if not hasattr(fio_ref.factory.root_obj, 'exists'):
return None
if not fio_ref.factory.root_obj.exists(_url.decode('utf-8')):
error.contents.value=GF_URL_ERROR
return None
do_delete=False
if mode=="unref":
fio_ref.nb_refs-=1
fio_ref.factory.all_refs-=1
if fio_ref.nb_refs:
return _fio_ref
do_delete=True
if mode=="close":
if fio_ref.py_obj != None:
fio_ref.py_obj.close()
fio_ref.factory.all_refs-=1
if fio_ref.nb_refs:
return None
do_delete=True
if do_delete:
#file no longer used, can now be garbage collected
fio_ref.factory.gc_exclude.remove(fio_ref)
if not fio_ref.factory == fio_ref:
_libgpac.gf_fileio_del(fio_ref._gf_fio)
fio_ref._gf_fio = None
if not fio_ref.factory.all_refs and not len(fio_ref.factory.pending_urls) and not len(fio_ref.factory.gc_exclude):
_libgpac.gf_fileio_del(fio_ref.factory._gf_fio)
fio_ref.factory._gf_fio = None
fio_ref.factory.root_obj = None
return None
#this is an open() call
fio_url = _url.decode('utf-8')
#check if associated object exists, if not so we can use this instance
fio = None
if fio_ref.py_obj == None:
fio = fio_ref
if fio_ref in fio_ref.factory.pending_urls:
fio_ref.factory.all_refs-=1
fio_ref.factory.pending_urls.remove(fio_ref)
#check in pending urls
if fio == None:
for afio in fio_ref.factory.pending_urls:
a_url = _libgpac.gf_fileio_resource_url(afio._gf_fio).decode('utf-8')
if a_url == fio_url:
fio_ref.factory.all_refs-=1
fio_ref.factory.pending_urls.remove(afio)
fio = afio
break
#we need to create a new FileIO
created=False
if fio==None:
created=True
#read url, translate
if fio_url.startswith('gfio://')==True:
fio_url = _libgpac.gf_fileio_translate_url(_url).decode('utf-8')
#if we create a new FileIO, we need to resolve url, against parent URL
elif created:
fio_url = url_concatenate(_libgpac.gf_fileio_resource_url(fio_ref._gf_fio), _url)
if fio==None:
fio = FileIO(fio_url, fio_ref)
#create object, direct copy of the object passed at construction
fio.py_obj = copy.copy(fio.factory.root_obj)
res = fio.py_obj.open(fio_url, mode)
if res==True:
if not fio in fio.factory.gc_exclude:
fio.factory.gc_exclude.append(fio)
fio.factory.all_refs+=1
return fio._gf_fio
#failure to open a new created object, remove FileIO and let the object be garbage collected
if created:
_libgpac.gf_fileio_del(fio._gf_fio)
fio._gf_fio = None
error.contents.value=GF_URL_ERROR
return None
#fio write callback
@CFUNCTYPE(c_uint, _gf_fileio, POINTER(c_ubyte), c_uint)
def fileio_cbk_write(_fio, buf, size):
fio = cast(_libgpac.gf_fileio_get_udta(_fio), py_object).value
if numpy_support:
ar_data = np.ctypeslib.as_array(buf, shape=(size,))
ar_data.flags.writeable=False
return fio.py_obj.write(ar_data, size)
return fio.py_obj.write(buf, size)
#fio read callback
@CFUNCTYPE(c_uint, _gf_fileio, POINTER(c_ubyte), c_uint)
def fileio_cbk_read(_fio, buf, size):
fio = cast(_libgpac.gf_fileio_get_udta(_fio), py_object).value
if numpy_support:
ar_data = np.ctypeslib.as_array(buf, shape=(size,))
ar_data.flags.writeable=True
return fio.py_obj.read(ar_data, size)
return fio.py_obj.read(buf, size)
#fio seek callback
@CFUNCTYPE(c_uint, _gf_fileio, c_ulonglong, c_int)
def fileio_cbk_seek(_fio, pos, whence):
fio = cast(_libgpac.gf_fileio_get_udta(_fio), py_object).value
return fio.py_obj.seek(pos, whence)
#fio tell callback
@CFUNCTYPE(c_ulonglong, _gf_fileio)
def fileio_cbk_tell(_fio):
fio = cast(_libgpac.gf_fileio_get_udta(_fio), py_object).value
return fio.py_obj.tell()
#fio eof callback
@CFUNCTYPE(gf_bool, _gf_fileio)
def fileio_cbk_eof(_fio):
fio = cast(_libgpac.gf_fileio_get_udta(_fio), py_object).value
return fio.py_obj.eof()
##\endcond private
##
# \defgroup pyfileio_grp libgpac core tools
# \ingroup pycore_grp Python APIs
# \brief FileIO tools for libgpac.
#
# @{
## FileIO object for file IO callbacks from libgpac
#
#
# The FileIO object is used to create input or output interfaces in which GPAC will read or write.
# This allows generating content in python without any disk IO, or passing python data as input to GPAC without intermediate file.
#
# This object passed to the FileIO constructor must implement the following callbacks:
#
# \code boolean open(string URL, string mode)\endcode
#Opens the file in read or write mode.
#- URL: string containing file to open
#- mode: open mode (same as fopen)
#- return true if success, false otherwise
#
#\note There is no guarantee that the URL is checked for existence before calling open
#\note There is no guarantee that the first call to open is on the URL provided for the constructor (e.g. for DASH generation, this will depend on the DASH profile used which may require to write the manifest after one or more segments)
#
# \code void close()\endcode
#Closes the file
#
# \code int write(numpy buffer, unsigned long size)\endcode
#Writes the file
#- buffer: numpy array to fill if numpy support, ctypes.c_ubyte otherwise
#- size: number of bytes to write starting from first byte in buffer
#- return number of bytes written, at most the size of the array
#
# \code int read(numy buffer, unsigned long size)\endcode
#Reads the file
#- buffer: numpy array to read if numpy support, ctypes.c_ubyte otherwise
#- size: number of bytes to read
#- return number of bytes read, at most the size of the array
#
# \code void seek(unsigned long long pos, int whence)\endcode
#Seeks current position in file (same as fopen)
#- pos: position
#- whence: origin of the position
#- return 0 if no error, error code otherwise
#
# \code unsigned long long tell()\endcode
#Gets current position in file (same as ftell)
#- pos: position
#- return current position
#
# \code boolean eof()\endcode
#Checks if the current position is at the end of the file (same as feof)
#- return true if file end reached, false otherwise
#
# \code boolean exists(string URL)\endcode
#Checks if the given URL exists. This callback is optional
#- return true if associated URL exists, false otherwise
#
#\warning All these callbacks MUST perform synchronously
#
# The URL passed to the constructor indentifies the file name wrapped.
# Some file types such as HLS or DASH manifest may imply reading or writing several files.
# To handle these cases, the object passed to the constructor will be cloned for each call to open().
#
#\warning This object is a shallow copy of the factory object, not an empty object (difference with NodeJS bindings).
#
# All FileIO callbacks will be done in the main thread.
#
class FileIO:
## constructor for FileIO
#\param url url for this fileIO factory
#\param obj instance of the class used for IOs. This instance will be cloned (shallow copy) for each new file to open
def __init__(self, url, obj):
if 0:
## the underlying gfio:// URL to be provided to GPAC
self.url = 0
##\cond private
_libgpac.user_init = True
self.nb_refs=0
self.py_obj = None
#this is internal constructor call from open
if obj.__class__.__name__=="FileIO":
self.factory = obj.factory
#this is regular constructor call
else:
if hasattr(obj, 'open')==False:
raise Exception('No open function on FileIO callback')
if hasattr(obj, 'close')==False:
raise Exception('No close function on FileIO callback')
if hasattr(obj, 'write')==False:
raise Exception('No write function on FileIO callback')
if hasattr(obj, 'read')==False:
raise Exception('No read function on FileIO callback')
if hasattr(obj, 'seek')==False:
raise Exception('No see function on FileIO callback')
if hasattr(obj, 'tell')==False:
raise Exception('No tell function on FileIO callback')
self.factory = self
self.factory.all_refs=0
self.factory.pending_urls=[]
#keep all created file IOs still useful here to prevent garbage collection
self.factory.gc_exclude=[]
self.factory.root_obj = obj
self._gf_fio = _libgpac.gf_fileio_new(url.encode('utf-8'), py_object(self), fileio_cbk_open, fileio_cbk_seek, fileio_cbk_read, fileio_cbk_write, fileio_cbk_tell, fileio_cbk_eof, None )
if self._gf_fio==None:
raise Exception('Failed to create FileIO for ' + url)
#ask for all file IOs to happen on main thread
_libgpac.gf_fileio_tag_main_thread(self._gf_fio)
@property
def url(self):
_url = _libgpac.gf_fileio_url(self._gf_fio)
if _url==None:
return None
return _url.decode('utf-8')
##\endcond private
## @}
#