Example #1
0
    def createPlaylistDock(self):
        #        from playlist import PlayList
        self.next_id = 0
        #        self.playlist = PlayList(self, None)
        self.propertydock = Q.QDockWidget()
        self.propertydock.setWindowTitle("Playlist")
        self.propertydock.setFeatures(Q.QDockWidget.DockWidgetFloatable
                                      | Q.QDockWidget.DockWidgetMovable)
        tw = Q.QTabWidget(parent=self.propertydock)
        player = self.playerwidget
        player._property_model = AVTreePropertyModel(player=player,
                                                     parent=player)
        if self._use_tree:
            tv = Q.QTreeView()
            tv.setModel(player._property_model)
            tw.addTab(tv, 'tree')
            tv.header().setSectionResizeMode(Q.QHeaderView.Stretch)

#        player._flat_model     = AVFlatPropertyModel(player=player, parent=player)
#        if self._use_table:
#            self.propertymodel= AVFlatPropertyModel(player=self.playerwidget,parent=self)
#            self.propertyview = Q.QTableView(self.propertydock)
#            self.propertyview.setModel(player._flat_model)
#            tw.addTab(self.propertyview, 'table')
#            self.propertyview.horizontalHeader().setSectionResizeMode(Q.QHeaderView.Stretch)

        self.propertydock.setWidget(tw)
        tw.show()
        tw.parent().adjustSize()
        tw.parent().update()
        self.propertydock.show()
Example #2
0
 def index(self, row, column, parent):
     if not self.hasIndex(row, column, parent):
         return Q.QModelIndex()
     if not parent.isValid():
         parentItem = self._rootItem
     else:
         parentItem = parent.internalPointer()
     childItem = parentItem.child(row)
     if childItem:
         return self.createIndex(row, column, childItem)
     else:
         return Q.QModelIndex()
Example #3
0
    def parent(self, index):
        if not index:
            return Q.QModelIndex()
        if not index.isValid():
            return Q.QModelIndex()
        childItem = index.internalPointer()
        if not childItem:
            return Q.QModelIndex()

        parentItem = childItem.parent
        if not parentItem:
            return Q.QModelIndex()
        if parentItem is self._rootItem:
            return Q.QModelIndex()
        return self.indexForItem(parentItem)
Example #4
0
 def __init__(self, *args, **kwargs):
     player = kwargs.pop('player', None)
     self._player = player
     super().__init__(*args, **kwargs)
     self._headerData = ["name", "value"]
     self._rootItem = TreeItem(name='root', player=player)
     self.setupModelData(player)
     print(self.__class__.__name__, 'rowCount() is ',
           self.rowCount(Q.QModelIndex()))
Example #5
0
    def __init__(self, *args, fp=None, **kwargs):
        super().__init__(*args, **kwargs)
        self._timer = Q.QTimer()
        #        self._timer.setInterval(int(1000/30))
        self._timer.setTimerType(Q.Qt.PreciseTimer)

        tw = Q.QTabWidget(parent=self)
        cw = CtrlPlayer(*args, parent=self, **kwargs)
        tw.addTab(cw, "video")
        cw.childwidget.resize(self.size())
        player = cw.childwidget
        #        player._property_model = AVTreePropertyModel(player=player,parent=player)
        #        tv = Q.QTreeView()
        #        tv.setModel(player._property_model)
        #        tw.addTab(tv,"properties")
        tw.setVisible(True)
        self.widget = cw
        self.playerwidget = player

        #        self._timer.timeout.connect(self.update)
        #        self._timer.timeout.connect(cw.update)
        self._timer.timeout.connect(cw.childwidget.update)

        #        self.widget = CtrlPlayer(fp=fp,parent=self)
        #        self.playerwidget = self.widget.childwidget
        self.setCentralWidget(tw)

        #        self.toolbar = Q.QDockWidget()
        #        self.toolbar.setFeatures(Q.QDockWidget.DockWidgetFloatable| Q.QDockWidget.DockWidgetMovable)
        #        self.toolbar.setWidget(toolbargroup)
        #        self.addDockWidget(Q.Qt.BottomDockWidgetArea,self.toolbar)
        #        self.toolbar.show()

        #    def finishPlaylist(self):
        #        self.createPlaylistDock()
        #        self.addDockWidget(Q.Qt.LeftDockWidgetArea,self.propertydock)
        #        self.propertydock.fileMenu =
        fileMenu = self.menuBar().addMenu("&File")
        #        fileMenu = self.propertydock.fileMenu
        fileMenu.addAction("&Open...", self.widget.openFile, "Ctrl+O")
        fileMenu.addAction("O&pen Url...", self.widget.openUrl, "Ctrl+Shift+O")
        fileMenu.addAction("E&xit", self.close, "Ctrl+Q")
Example #6
0
    def openFile(self, near=None):
        fileDialog = Q.QFileDialog(self)
        fileDialog.setAcceptMode(Q.QFileDialog.AcceptOpen)
        fileDialog.setFileMode(Q.QFileDialog.ExistingFiles)
        fileDialog.setFilter(Q.QDir.Hidden | Q.QDir.AllEntries | Q.QDir.System)
        fileDialog.setViewMode(Q.QFileDialog.Detail)
        if near is not None:
            if isinstance(near, str):
                near = Q.QFileInfo(near)
            if isinstance(near, Q.QFileInfo):
                if near.isDir():
                    near = Q.QDir(near.filePath())
                else:
                    near = near.dir()
        if not isinstance(near, Q.QDir):
            near = Q.QDir.home()

        fileDialog.setDirectory(near.canonicalPath())
        if fileDialog.exec():
            if fileDialog.selectedFiles():
                for filePath in fileDialog.selectedFiles():
                    print("\n" + filePath + "\n")
                    self.childwidget.try_command("loadfile", str(filePath),
                                                 "append-play")
Example #7
0
 def reconfig(self, width, height):
     if width > 0 and width < 2**16:
         self.vwidth = width
     if height > 0 and height < 2**16:
         self.vheight = height
     if self.vwidth and self.vheight:
         self.childwidget.img_width = self.vwidth
         self.childwidget.img_height = self.vheight
         if (not self.sized_once) and width:
             self.childwidget.setMinimumSize(
                 Q.QSize(self.vwidth, self.vheight))
             self.adjustSize()
             self.adjustSize()
             parent = self.parent()
             if parent:
                 parent.adjustSize()
                 parent = parent.parent()
                 if parent:
                     parent.adjustSize()
             self.window().update()
             if not self.sized_once:
                 self.sized_once = True
                 self.show()
Example #8
0
parser.add_argument('path')
args = parser.parse_args()

container = av.open(args.path)
stream = next(s for s in container.streams if s.type == 'audio')

fifo = av.AudioFifo()
resampler = av.AudioResampler(
    format=av.AudioFormat('s16').packed,
    layout='stereo',
    rate=48000,
)



qformat = Q.AudioFormat()
qformat.setByteOrder(Q.AudioFormat.LittleEndian)
qformat.setChannelCount(2)
qformat.setCodec('audio/pcm')
qformat.setSampleRate(48000)
qformat.setSampleSize(16)
qformat.setSampleType(Q.AudioFormat.SignedInt)

output = Q.AudioOutput(qformat)
output.setBufferSize(2 * 2 * 48000)

device = output.start()

print(qformat, output, device)

def decode_iter():
Example #9
0
 def rowCount(self, parent=Q.QModelIndex()):
     return len(self._props)
Example #10
0
    def __init__(self, *args, **kwargs):
        fp = kwargs.pop('fp', None)
        use_tabs = kwargs.pop('tabs', True)
        super().__init__(*args, **kwargs)
        self.setSizePolicy(
            Q.QSizePolicy(Q.QSizePolicy.MinimumExpanding,
                          Q.QSizePolicy.MinimumExpanding, Q.QSizePolicy.Frame))
        childwidget = self.childwidget = AVPlayer(fp=fp, parent=self)
        childwidget.setSizePolicy(
            Q.QSizePolicy(Q.QSizePolicy.MinimumExpanding,
                          Q.QSizePolicy.MinimumExpanding, Q.QSizePolicy.Label))

        self.splitter = Q.QSplitter()
        self.splitter.setOrientation(Q.Qt.Vertical)
        self.layout = Q.QVBoxLayout()
        self.splitter.addWidget(self.childwidget)
        self.layout.addWidget(self.splitter)
        self.setLayout(self.layout)

        controls_widget = Q.QWidget()
        controls_layout = Q.QVBoxLayout()
        controls_widget.setLayout(controls_layout)
        self.splitter.addWidget(controls_widget)
        time_layout = Q.QVBoxLayout()

        timespin_layout = Q.QHBoxLayout()

        self.timespin = Q.QDoubleSpinBox()
        self.timespin.setFixedWidth(120)
        self.timespin.setDecimals(5)
        self.timespin.setSingleStep(1e-2)
        timespin_layout.addWidget(self.timespin)

        step_back = Q.QPushButton("step -")
        step_back.clicked.connect(
            lambda: self.childwidget.try_command('frame-back-step'))
        timespin_layout.addWidget(step_back)

        step_forward = Q.QPushButton("step +")
        step_forward.clicked.connect(
            lambda: self.childwidget.try_command('frame-step'))
        timespin_layout.addWidget(step_forward)

        timespin_layout.addStretch(-1)

        time_layout.addLayout(timespin_layout)

        self.timeline = Q.QSlider(Q.Qt.Horizontal)
        self.timeline.setSizePolicy(Q.QSizePolicy.Expanding,
                                    Q.QSizePolicy.Preferred)
        self.timeline.setEnabled(True)
        self.timeline.sliderReleased.connect(
            lambda: self.onTimelineChanged(self.timeline.value()))
        time_layout.addWidget(self.timeline)

        controls_layout.addLayout(time_layout)

        childwidget.time_pos.valueChanged.connect(self.onTime_posChanged)
        self.timeline.valueChanged.connect(self.onTimelineChanged)
        self.timespin.valueChanged.connect(self.onTimespinChanged)
        childwidget.duration.valueChanged.connect(self.onDurationChanged)

        self.speed = Q.QSlider(Q.Qt.Horizontal)
        self.speed.setSizePolicy(Q.QSizePolicy.Expanding,
                                 Q.QSizePolicy.Preferred)
        self.speed_base = 1e8
        self.speed_max = 5.0
        self.speed_pow = 5.0**(self.speed_base**-1)
        self.speed.setValue(self.speed_base)
        self.speed.setRange(16, 2 * self.speed_base)
        self.speed.setEnabled(True)
        self.speed.valueChanged.connect(self.speedChanged)
        childwidget.speed.valueChanged.connect(self.onSpeedChanged)

        play_button = Q.QPushButton("play/pause")
        play_button.clicked.connect(self.pause)
        self.play_button = play_button

        rate_down_button = Q.QPushButton("rate -")
        rate_down_button.clicked.connect(lambda: self.rate_adj(1. / 1.1))

        rate_down_tmp_button = Q.QPushButton(" tmp -")
        rate_down_tmp_button.pressed.connect(lambda: self.temp_rate(1. / 1.1))
        rate_down_tmp_button.released.connect(self.temp_rate_release)

        rate_up_button = Q.QPushButton("rate +")
        rate_up_button.clicked.connect(lambda: self.rate_adj(1.1))

        rate_up_tmp_button = Q.QPushButton(" tmp +")
        rate_up_tmp_button.pressed.connect(lambda: self.temp_rate(1.1))
        rate_up_tmp_button.released.connect(self.temp_rate_release)

        rate_down_layout = Q.QVBoxLayout()
        rate_down_layout.addWidget(rate_down_button)
        rate_down_layout.addWidget(rate_down_tmp_button)

        play_speed_layout = Q.QVBoxLayout()
        play_speed_layout.addWidget(play_button)
        play_speed_layout.addWidget(self.speed)

        rate_up_layout = Q.QVBoxLayout()
        rate_up_layout.addWidget(rate_up_button)
        rate_up_layout.addWidget(rate_up_tmp_button)

        control_layout = Q.QHBoxLayout()
        control_layout.addLayout(rate_down_layout)
        control_layout.addLayout(play_speed_layout)
        control_layout.addLayout(rate_up_layout)

        childwidget.video_params.valueChanged.connect(
            self.onVideo_paramsChanged)
        childwidget.reconfig.connect(self.reconfig)
        childwidget.novid.connect(self.novid)
        childwidget.hasvid.connect(self.hasvid)
        self.sized_once = False
        self.reconfig(640, 480)
        self.sized_once = False

        controls_layout.addLayout(control_layout)

        toolbarlayout = Q.QVBoxLayout()
        cmdlinelayout = Q.QHBoxLayout()
        histloglayout = Q.QHBoxLayout()
        self.histline = Q.QPlainTextEdit()
        self.histline.setReadOnly(True)
        self.histline.setCenterOnScroll(False)
        self.histline.setSizePolicy(Q.QSizePolicy.Expanding,
                                    Q.QSizePolicy.Preferred)
        self.logline = Q.QPlainTextEdit()
        self.logline.setReadOnly(True)
        self.logline.setSizePolicy(Q.QSizePolicy.Expanding,
                                   Q.QSizePolicy.Preferred)

        self.cmdline = cmdline = CmdLine()
        er_label = self.er_label = Q.QLabel()
        pr_label = self.pr_label = Q.QLabel()
        fr_label = self.fr_label = Q.QLabel()
        sr_label = self.sr_label = Q.QLabel()
        self._timer = Q.QTimer(self)
        self._timer.setInterval(int(1000 / 5))
        self._timer.setTimerType(Q.Qt.CoarseTimer)

        def updateLabels():
            self.pr_label.setText('paint rate: {:.6f}'.format(
                self.childwidget.paintRate))
            self.er_label.setText('event rate: {:.6f}'.format(
                self.childwidget.eventRate))
            self.fr_label.setText('frame rate: {:.6f}'.format(
                self.childwidget.frameRate))
            self.sr_label.setText('swap rate: {:.6f}'.format(
                self.childwidget.swapRate))

        self._timer.timeout.connect(updateLabels, Q.Qt.QueuedConnection)
        self._timer.start()
        #        self.cmdline = cmdline = CmdLine(self.toolbargroup)
        self.cmdline.setSizePolicy(Q.QSizePolicy.Expanding,
                                   Q.QSizePolicy.Preferred)
        cmdlinelayout.addWidget(self.cmdline)
        cmdlinelayout.addWidget(er_label)
        cmdlinelayout.addWidget(pr_label)
        cmdlinelayout.addWidget(fr_label)
        cmdlinelayout.addWidget(sr_label)
        toolbarlayout.addLayout(cmdlinelayout)
        histloglayout.addWidget(self.histline)
        histloglayout.addWidget(self.logline)

        if use_tabs:
            tw = Q.QTabWidget(parent=self)
            tg = Q.QWidget()
            tg.setLayout(histloglayout)
            tw.addTab(tg, "history/log")
            childwidget._property_model = AVTreePropertyModel(
                player=childwidget, parent=childwidget)
            tv = Q.QTreeView()
            tv.setModel(childwidget._property_model)
            tw.addTab(tv, "properties")
            tw.setVisible(True)
            toolbarlayout.addWidget(tw)
        else:
            toolbarlayout.addLayout(histloglayout)
        controls_layout.addLayout(toolbarlayout)
        cmdline.submitted.connect(self.onCmdlineAccept,
                                  Q.Qt.UniqueConnection | Q.Qt.AutoConnection)
        cmdline.historyChanged.connect(self.redoHistory)
        self.childwidget.logMessage.connect(self.onLogMessage)
Example #11
0
class CmdLine(Q.QLineEdit):
    submitted = Q.pyqtSignal(str, bool)
    historyPosChanged = Q.pyqtSignal(int)
    historyChanged = Q.pyqtSignal()
    historyAppended = Q.pyqtSignal(str)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._history = collections.deque()
        self._history_pos = 0
        self.historyAppended.connect(self.historyChanged,
                                     Q.Qt.UniqueConnection)
        self.returnPressed.connect(self.onReturnPressed, Q.Qt.UniqueConnection)

    def keyPressEvent(self, evt):
        if evt.modifiers() == Q.Qt.ControlModifier:
            if evt.key() == Q.Qt.Key_P or evt.key() == Q.Qt.Key_Up:
                if self.historyPos + 1 < len(self._history):
                    self.historyPos += 1
                    self.setText(self._history[self.historyPos])
                    return
            elif evt.key() == Q.Qt.Key_N or evt.key() == Q.Qt.Key_Down:
                if self.historyPos > 0 and self._history:
                    self.historyPos -= 1
                    self.setText(self._history[self.historyPos - 1])
                else:
                    self.historyPos = -1
                    return
            elif evt.key() in (Q.Qt.Key_C, Q.Qt.Key_U):
                self.historyPos = -1
                self.clear()
                return
        super().keyPressEvent(evt)

    @property
    def history(self):
        return self._history

    @property
    def historyPos(self):
        return self._history_pos

    @historyPos.setter
    def historyPos(self, pos):
        new_pos = max(min(len(self._history) - 1, int(pos)), -1)
        if new_pos != self._history_pos:
            self._history_pos = new_pos
            if new_pos >= 0:
                self.setText(self.history[new_pos])
            else:
                self.clear()
            self.historyPosChanged.emit(new_pos)

    def historyAppend(self, text):
        if (text):
            self._history.appendleft(text)
            self.historyAppended.emit(text)

    def onReturnPressed(self):
        text = self.text().strip()
        if self._history and self._history[0] == text:
            text = ''
        if text:
            self.historyAppend(text)
            self._history_pos = -1
            self.clear()
            self.historyPosChanged.emit(-1)
            self.submitted.emit(text, False)
        elif self._history:
            self._history_pos = -1
            self.clear()
            self.historyPosChanged.emit(-1)
            self.submitted.emit(self._history[0], False)
Example #12
0
 def sizeHint(self):
     return Q.QSize(self.img_width, self.img_height)
Example #13
0
class AVProperty(Q.QObject):
    mpv = __import__('mpv')
    valueChanged = Q.pyqtSignal(object)
    _value = None

    @property
    def propertyName(self):
        return self.objectName()

    @property
    def context(self):
        return self.__context__

    @Q.pyqtSlot()
    def value(self):
        return self._value


#        return getattr(self.context,self.objectName())

    @Q.pyqtSlot(object)
    def setValue(self, value):
        if self._value != value:
            self._value = value
            setattr(self.context, self.objectName(), value)

    def _emitValueChanged(self, value):
        if self._value != value:
            self._value = value
            self.valueChanged.emit(value)

    def __index__(self):
        return int(self)

    def __str__(self):
        return str(self.value())

    def __bytes__(self):
        return bytes(self.value())

    def __init__(self, prop, ctx, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__context__ = ctx
        prop = ctx.attr_name(prop)
        self.setObjectName(prop)
        #        ctx.request_event(ctx.mpv.EventType.property_change,True)
        try:
            self._value = self.context.get_property(self.objectName())
        except:
            self._value = None
        reply_userdata = lambda val: self._emitValueChanged(val)
        ctx_ref = weakref.ref(ctx)

        def unobserve_cb(val):
            ctx = ctx_ref()
            try:
                ctx.unobserve_property(val)
            except:
                pass

        self.reply_userdata = reply_userdata
        ctx.observe_property(prop, reply_userdata)
        self._finalizer = weakref.finalize(self, unobserve_cb, reply_userdata)
Example #14
0
 def columnCount(self, parent=Q.QModelIndex()):
     return 2
Example #15
0
    #    fmt.setProfile(fmt.CoreProfile)
    #    fmt.setDepthBufferSize(24)
    #    fmt.setRedBufferSize(8)
    #    fmt.setGreenBufferSize(8)
    #    fmt.setBlueBufferSize(8)
    #    fmt.setAlphaBufferSize(8)
    #    fmt.setSwapBehavior(fmt.TripleBuffer)
    #    fmt.setSwapBehavior(fmt.SingleBuffer)
    #    fmt.setRenderableType(fmt.OpenGL)
    #    fmt.setOption(fmt.DeprecatedFunctions,False)
    #    fmt.setOption(fmt.ResetNotification,True)
    #    Q.QSurfaceFormat.setDefaultFormat(fmt)

    #    Q.QCoreApplication.setAttribute(Q.Qt.AA_ShareOpenGLContexts)

    app = Q.QApplication([])

    mw = Canvas()
    if args.forcerate is not None and args.forcerate:
        mw.forcedFrameRate = args.forcerate
#    else:
#        mw.forcedFrameRate = None

    ap = mw.playerwidget

    if args.getprocaddress:
        ap._get_proc_address = args.getprocaddress
    if args.getprocaddressquiet:
        ap._get_proc_address_debug = False

    def dump_fmt(fmt):
Example #16
0
parser.add_argument('-f', '--format')
parser.add_argument('path')
args = parser.parse_args()


def _iter_images():
    video = av.open(args.path, format=args.format)
    stream = next(s for s in video.streams if s.type == b'video')
    for packet in video.demux(stream):
        for frame in packet.decode():
            yield frame.reformat(frame.width, frame.height, 'rgb24')


image_iter = _iter_images()

app = Q.Application([])

glwidget = PlayerGLWidget()
glwidget.setFixedWidth(WIDTH)
glwidget.setFixedHeight(HEIGHT)
glwidget.show()
glwidget.raise_()

start_time = 0
count = 0

timer = Q.Timer()
timer.setInterval(1000 / 30)


@timer.timeout.connect
Example #17
0
import posix,posixpath

if sys.version_info.major > 2:
    basestring = str
from modproxy import ModuleProxy
from glproxy  import gl, glx
from qtproxy import Q, QW, QG
#from PyQt5 import Qt as Q, QtWidgets as QW, QtGui as QG

from topwindow import TopWindow
from player import Player

if __name__ == '__main__':
#    Q.QSurfaceFormat.setDefaultFormat(fmt)
    app = Q.QApplication(sys.argv)

#    player = Player()
    args = list()
    kwargs = dict()
    for arg in sys.argv[1:]:
        if '=' in arg:
            parts = arg.partition('=')
            kwargs[parts[0]] = parts[2]
        else:
            args.append(arg)

    with SignalWakeupHandler(app):
        signal.signal(signal.SIGINT, lambda *a:app.quit())

        win = TopWindow(*args, **kwargs)
Example #18
0
class AVPlayer(Q.QOpenGLWidget):
    pfl = Q.QOpenGLVersionProfile()
    pfl.setVersion(4, 1)
    pfl.setProfile(Q.QSurfaceFormat.CoreProfile)

    base_options = {
        'input-default-bindings': True,
        'input_vo_keyboard': True,
        'gapless_audio': True,
        'osc': False,
        'load_scripts': True,
        'ytdl': True,
        'vo': 'opengl-cb',
        'opengl-fbo-format': 'rgba32f',
        'alpha': True
        #        ,'opengl-es':True
        ,
        'opengl-swapinterval': 1,
        'opengl-waitvsync': False,
        'opengl-vsync-fences': 6,
        'tscale-radius': 4.0,
        'tscale-wparam': 1.0,
        'tscale': 'gaussian',
        'interpolation': True,
        'opengl-backend': 'drm',
        'video-sync': 'display-resample',
        'display-fps': 60.0,
        'interpolation-threshold': 0.0,
        'interpolation': True
        #        ,'vo-vaapi-scaling':'nla'
        #        ,'vo-vaapi-scaled-osd':True
        #        ,'vo-vdpau-hqscaling':5
        #        ,'vo-vdpau-deint':True
        #        ,'vd-lavc-fast':True
        #        ,'vd-lavc-show-all':True
        ,
        'hr-seek': 'yes'
        #        ,'hr-seek-framedrop':True
        ,
        'hwdec-preload': True,
        'hwdec': 'yes',
        'opengl-hwdec-interop': 'drmprime-drm'
    }
    _reportFlip = False
    _reportedFlip = False
    _externalDrive = False
    _get_proc_address = 'ctypes'
    _get_proc_address_debug = True

    novid = Q.pyqtSignal()
    hasvid = Q.pyqtSignal()
    reconfig = Q.pyqtSignal(int, int)
    fullscreen = Q.pyqtSignal(bool)
    wakeup = Q.pyqtSignal()
    mpv_event = Q.pyqtSignal()
    logMessage = Q.pyqtSignal(object)
    just_die = Q.pyqtSignal()
    paintRateChanged = Q.pyqtSignal(object)
    eventRateChanged = Q.pyqtSignal(object)
    frameRateChanged = Q.pyqtSignal(object)
    swapRateChanged = Q.pyqtSignal(object)
    openglInitialized = Q.pyqtSignal(object)
    mpv = __import__('mpv')

    def get_property(self, prop):
        try:
            prop_name = self.m.attr_name(prop)
        except:
            return
        if not prop_name:
            return
        binding = self.findChild(AVProperty, prop_name)
        if binding:
            return binding
        prop_object = AVProperty(prop_name, ctx=self.m, parent=self)
        setattr(self, prop, prop_object)
        if not hasattr(self, prop_name):
            setattr(self, prop_name, prop_object)
        return prop_object

    def __getattr__(self, prop):
        try:
            prop_name = self.m.attr_name(prop)
        except:
            #            return super().__getattr__(prop)
            raise AttributeError
        if not prop_name:
            raise AttributeError
        binding = self.findChild(AVProperty, prop_name)
        if binding:
            return binding
        prop_object = AVProperty(prop_name, ctx=self.m, parent=self)
        setattr(self, prop, prop_object)
        if not hasattr(self, prop_name):
            setattr(self, prop_name, prop_object)
        return prop_object

    def sizeHint(self):
        return Q.QSize(self.img_width, self.img_height)
#        return self.get_property(prop)

    @staticmethod
    def get_options(*args, **kwargs):
        options = kwargs
        media = list()
        for arg in args:
            if arg.startswith('--'):
                arg, _, value = arg[2:].partition('=')
                options[arg] = value or True
                continue
            media.append(arg)
        return options, media

    def __init__(self, *args, fp=None, **kwargs):
        super().__init__(*args, **kwargs)

        self.paintTimes = deque(maxlen=32)
        self.frameTimes = deque(maxlen=32)
        self.swapTimes = deque(maxlen=32)
        self.eventTimes = deque(maxlen=32)
        self.setMouseTracking(True)
        self._updated = False
        self.event_handler_cache = weakref.WeakValueDictionary()
        self.prop_bindings = dict()
        import locale
        locale.setlocale(locale.LC_NUMERIC, 'C')
        options = self.base_options.copy()
        new_options, media = self.get_options(*args, **kwargs)
        options.update(new_options)
        options[
            'msg-level'] = 'all=status,vd=debug,hwdec=debug,vo=debug,video=v,opengl=debug'
        options['af'] = 'rubberband=channels=apart:pitch=quality'
        self.new_frame = False

        mpv = self.mpv
        self.m = mpv.Context(**options)
        m = self.m
        self.just_die.connect(m.shutdown, Q.Qt.DirectConnection)
        self.destroyed.connect(self.just_die, Q.Qt.DirectConnection)

        self.m.set_log_level('terminal-default')
        self.m.set_wakeup_callback_thread(self.onEvent)
        #        self.m.set_wakeup_callback(self.onEvent)
        self.m.request_event(self.mpv.EventType.property_change, True)
        self.m.request_event(self.mpv.EventType.video_reconfig, True)
        self.m.request_event(self.mpv.EventType.file_loaded, True)
        self.m.request_event(self.mpv.EventType.log_message, True)

        self.img_width = 64
        self.img_height = 64
        self.img_update = None

        self.tex_id = 0
        self.fbo = None
        self._width = self.img_width
        self._height = self.img_height
        if isinstance(fp, pathlib.Path):
            fp = fp.resolve().absolute().as_posix()
        elif isinstance(fp, Q.QFileInfo):
            fp = fp.canonicalFilePath()
        elif isinstance(fp, Q.QUrl):
            fp = fp.toString()
        Q.QTimer.singleShot(0, self.update)
        if fp:
            Q.QTimer.singleShot(0, (
                lambda: self.m.command('loadfile', fp, 'append', _async=True)))

    def command(self, *args, **kwargs):
        self.m.command(*args, **kwargs)

    def command_string(self, cmdlist):
        try:
            self.m.command_string(cmdlist)
        except:
            pass

    def try_command(self, *args, **kwargs):
        kwargs.setdefault('_async', True)
        try:
            self.m.command(*args)
        except self.mpv.MPVError:
            pass

    @staticmethod
    def get_options(*args, **kwargs):
        options = kwargs
        media = list()
        for arg in args:
            if arg.startswith('--'):
                arg, _, value = arg[2:].partition('=')
                options[arg] = value or True
                continue
            media.append(arg)
        return options, media

    @Q.pyqtProperty(float)
    def paintRate(self):
        if len(self.paintTimes) < 2:
            return 0
        else:
            return 1e6 * (len(self.paintTimes) - 1) / (self.paintTimes[-1] -
                                                       self.paintTimes[0])

    def paintTimeAppend(self, time):
        self.paintTimes.append(time)
        self.paintRateChanged.emit(self.paintRate)

    @Q.pyqtProperty(float)
    def frameRate(self):
        if len(self.frameTimes) < 2:
            return 0
        else:
            return 1e6 * (len(self.frameTimes) - 1) / (self.frameTimes[-1] -
                                                       self.frameTimes[0])

    def frameTimeAppend(self, time):
        self.frameTimes.append(time)
        self.frameRateChanged.emit(self.frameRate)

    @Q.pyqtProperty(float)
    def swapRate(self):
        if len(self.swapTimes) < 2:
            return 0
        else:
            return 1e6 * (len(self.swapTimes) - 1) / (self.swapTimes[-1] -
                                                      self.swapTimes[0])

    def swapTimeAppend(self, time):
        self.swapTimes.append(time)
        self.swapRateChanged.emit(self.swapRate)

    @Q.pyqtProperty(float)
    def eventRate(self):
        if len(self.eventTimes) < 2:
            return 0
        else:
            return 1e6 * (len(self.eventTimes) - 1) / (self.eventTimes[-1][0] -
                                                       self.eventTimes[0][0])

    def eventTimeAppend(self, time, type=None):
        self.eventTimes.append((time, type))
        self.eventRateChanged.emit(self.eventRate)

    @Q.pyqtSlot(object)
    def onEventData(self, event):
        m = self.m
        if not m:
            return
        self.eventTimeAppend(self.m.time, event.id)
        if event.id is self.mpv.EventType.shutdown:
            print("on_event -> shutdown")
            self.just_die.emit()
        elif event.id is self.mpv.EventType.idle:
            self.novid.emit()
        elif event.id is self.mpv.EventType.start_file:
            self.hasvid.emit()
        elif event.id is self.mpv.EventType.file_loaded:
            ao = m.ao
            if ao and ao[0]['name'] in ('null', 'none'):
                m.aid = 0
            else:
                try:
                    if m.current_ao in ('null', 'none'):
                        m.aid = 0
                except self.mpv.MPVError as e:
                    if e.code == self.mpv.Error.property_unavailable:
                        m.aid = 0

            if not m.aid:
                if m.af:
                    self.af = m.af
                m.af = ""
            else:
                if self.af and not m.af:
                    m.af = self.af
#        self.durationChanged.emit(m.duration)
        elif event.id is self.mpv.EventType.log_message:
            self.logMessage.emit(event.data)
            #        print(event.data.text,)
        elif (event.id is self.mpv.EventType.end_file
              or event.id is self.mpv.EventType.video_reconfig):
            try:
                self.m.vid = 1
                self.reconfig.emit(self.m.dwidth, -self.m.dheight)
            except self.mpv.MPVError as ex:
                self.reconfig.emit(None, None)
        elif event.id is self.mpv.EventType.property_change:
            oname = event.data.name
            data = event.data.data
            if event.reply_userdata:
                try:
                    event.reply_userdata(data)
                except:
                    pass
            elif event.data.name == 'fullscreen':
                pass

    @Q.pyqtSlot()
    def onEvent(self):
        m = self.m
        if not m:
            return
        while True:
            event = m.wait_event(0)
            if event is None:
                print("Warning, received a null event.")
                return
            elif event.id is self.mpv.EventType.none:
                return
            else:
                try:
                    self.onEventData(event)
                except Exception as e:
                    print(e)

    @Q.pyqtSlot()
    def onWakeup(self):
        #        if not self._externalDrive:
        self.frameTimeAppend(self.m.time)
        self.update()

    def initializeGL(self):
        print('initialize GL')

        if self._get_proc_address is 'ctypes':
            import ctypes, ctypes.util
            lgl = ctypes.cdll.LoadLibrary(ctypes.util.find_library('GL'))
            get_proc_address = lgl.glXGetProcAddress
            get_proc_address.restype = ctypes.c_void_p
            get_proc_address.argtypes = [ctypes.c_char_p]
            _get_proc_address = lambda name: get_proc_address(
                name if isinstance(name, bytes) else name.encode('latin1'))
        else:
            qgl_get_proc_address = Q.QGLContext.currentContext().getProcAddress
            _get_proc_address = lambda name: int(
                qgl_get_proc_address(
                    name.decode('latin1')
                    if isinstance(name, bytes) else name))

        if self._get_proc_address_debug:

            def getprocaddr(name):
                res = _get_proc_address(name)
                print('{} -> address {}'.format(name, res))
                return res
        else:
            getprocaddr = _get_proc_address

        self.ogl = self.m.opengl_cb_context
        self.ogl.init_gl(getprocaddr, None)

        weakref.finalize(self, lambda: self.ogl.set_update_callback(None))
        self.wakeup.connect(self.onWakeup,
                            Q.Qt.QueuedConnection | Q.Qt.UniqueConnection)
        self.frameSwapped.connect(self.onFrameSwapped)
        self.ogl.set_update_callback(self.wakeup.emit)
        self.openglInitialized.emit(Q.QOpenGLContext.currentContext())

    @Q.pyqtSlot()
    def onFrameSwapped(self):
        self.swapTimeAppend(self.m.time)
        if self._updated:
            self._updated = False
        if self.reportFlip:
            self.ogl.report_flip(self.m.time)
            self._reportedFlip = True

    @property
    def reportFlip(self):
        return self._reportFlip

    @reportFlip.setter
    def reportFlip(self, val):
        if self._reportedFlip:
            return
        self._reportFlip = bool(val)

    @property
    def externalDrive(self):
        return self._externalDrive

    @externalDrive.setter
    def externalDrive(self, val):
        self._externalDrive = bool(val)

    def resizeGL(self, w, h):
        self._width = w
        self._height = h

    def paintGL(self):
        self.paintTimeAppend(self.m.time)
        self.ogl.draw(self.defaultFramebufferObject(), self._width,
                      -self._height)
        self._updated = True