From a36ab583f1ec163d621800cceb0188d74345ffc7 Mon Sep 17 00:00:00 2001 From: WyattBlue Date: Sun, 15 Oct 2023 01:50:19 -0400 Subject: [PATCH] Remove outdated example --- scratchpad/second_seek_example.py | 499 ------------------------------ 1 file changed, 499 deletions(-) delete mode 100644 scratchpad/second_seek_example.py diff --git a/scratchpad/second_seek_example.py b/scratchpad/second_seek_example.py deleted file mode 100644 index 4b184c56b..000000000 --- a/scratchpad/second_seek_example.py +++ /dev/null @@ -1,499 +0,0 @@ -""" -Note this example only really works accurately on constant frame rate media. -""" -from PyQt4 import QtGui -from PyQt4 import QtCore -from PyQt4.QtCore import Qt - -import sys -import av - - -AV_TIME_BASE = 1000000 - -def pts_to_frame(pts, time_base, frame_rate, start_time): - return int(pts * time_base * frame_rate) - int(start_time * time_base * frame_rate) - -def get_frame_rate(stream): - - if stream.average_rate.denominator and stream.average_rate.numerator: - return float(stream.average_rate) - if stream.time_base.denominator and stream.time_base.numerator: - return 1.0 / float(stream.time_base) - else: - raise ValueError("Unable to determine FPS") - -def get_frame_count(f, stream): - - if stream.frames: - return stream.frames - elif stream.duration: - return pts_to_frame(stream.duration, float(stream.time_base), get_frame_rate(stream), 0) - elif f.duration: - return pts_to_frame(f.duration, 1 / float(AV_TIME_BASE), get_frame_rate(stream), 0) - - else: - raise ValueError("Unable to determine number for frames") - -class FrameGrabber(QtCore.QObject): - - frame_ready = QtCore.pyqtSignal(object, object) - update_frame_range = QtCore.pyqtSignal(object, object) - - def __init__(self, parent=None): - super().__init__(parent) - self.file = None - self.stream = None - self.frame = None - self.active_time = None - self.start_time = 0 - self.pts_seen = False - self.nb_frames = None - - self.rate = None - self.time_base = None - - self.pts_map = {} - - def next_frame(self): - - frame_index = None - - rate = self.rate - time_base = self.time_base - - self.pts_seen = False - - for packet in self.file.demux(self.stream): - #print " pkt", packet.pts, packet.dts, packet - if packet.pts: - self.pts_seen = True - - for frame in packet.decode(): - - if frame_index is None: - - if self.pts_seen: - pts = frame.pts - else: - pts = frame.dts - - if not pts is None: - frame_index = pts_to_frame(pts, time_base, rate, self.start_time) - - elif not frame_index is None: - frame_index += 1 - - if not frame.dts in self.pts_map: - secs = None - - if not pts is None: - secs = pts * time_base - - self.pts_map[frame.dts] = secs - - - #if frame.pts == None: - - - - yield frame_index, frame - - - - @QtCore.pyqtSlot(object) - def request_time(self, second): - - frame = self.get_frame(second) - if not frame: - return - - rgba = frame.reformat(frame.width, frame.height, "rgb24", 'itu709') - #print rgba.to_image().save("test.png") - # could use the buffer interface here instead, some versions of PyQt don't support it for some reason - # need to track down which version they added support for it - self.frame = bytearray(rgba.planes[0]) - bytesPerPixel = 3 - img = QtGui.QImage(self.frame, rgba.width, rgba.height, rgba.width * bytesPerPixel, QtGui.QImage.Format_RGB888) - - #img = QtGui.QImage(rgba.planes[0], rgba.width, rgba.height, QtGui.QImage.Format_RGB888) - - #pixmap = QtGui.QPixmap.fromImage(img) - self.frame_ready.emit(img, second) - - def get_frame(self, target_sec): - - if target_sec != self.active_time: - return - print('seeking to', target_sec) - - rate = self.rate - time_base = self.time_base - - target_pts = int(target_sec / time_base) + self.start_time - seek_pts = target_pts - - - self.stream.seek(seek_pts) - - #frame_cache = [] - - last_frame = None - - for i, (frame_index, frame) in enumerate(self.next_frame()): - - - if target_sec != self.active_time: - return - - pts = frame.dts - if self.pts_seen: - pts = frame.pts - - if pts > target_pts: - break - - print(frame.pts, seek_pts) - last_frame = frame - - if last_frame: - - return last_frame - - - def get_frame_old(self, target_frame): - - if target_frame != self.active_frame: - return - print('seeking to', target_frame) - - seek_frame = target_frame - - rate = self.rate - time_base = self.time_base - - frame = None - reseek = 250 - - original_target_frame_pts = None - - while reseek >= 0: - - # convert seek_frame to pts - target_sec = seek_frame * 1 / rate - target_pts = int(target_sec / time_base) + self.start_time - - if original_target_frame_pts is None: - original_target_frame_pts = target_pts - - self.stream.seek(int(target_pts)) - - frame_index = None - - frame_cache = [] - - for i, (frame_index, frame) in enumerate(self.next_frame()): - - # optimization if the time slider has changed, the requested frame no longer valid - if target_frame != self.active_frame: - return - - print(" ", i, "at frame", frame_index, "at ts:", frame.pts, frame.dts, "target:", target_pts, 'orig', original_target_frame_pts) - - if frame_index is None: - pass - - elif frame_index >= target_frame: - break - - frame_cache.append(frame) - - # Check if we over seeked, if we over seekd we need to seek to a earlier time - # but still looking for the target frame - if frame_index != target_frame: - - if frame_index is None: - over_seek = '?' - else: - over_seek = frame_index - target_frame - if frame_index > target_frame: - - print(over_seek, frame_cache) - if over_seek <= len(frame_cache): - print("over seeked by %i, using cache" % over_seek) - frame = frame_cache[-over_seek] - break - - - seek_frame -= 1 - reseek -= 1 - print("over seeked by %s, backtracking.. seeking: %i target: %i retry: %i" % (str(over_seek), seek_frame, target_frame, reseek)) - - else: - break - - if reseek < 0: - raise ValueError("seeking failed %i" % frame_index) - - # frame at this point should be the correct frame - - if frame: - - return frame - - else: - raise ValueError("seeking failed %i" % target_frame) - - def get_frame_count(self): - - frame_count = None - - if self.stream.frames: - frame_count = self.stream.frames - elif self.stream.duration: - frame_count = pts_to_frame(self.stream.duration, float(self.stream.time_base), get_frame_rate(self.stream), 0) - elif self.file.duration: - frame_count = pts_to_frame(self.file.duration, 1 / float(AV_TIME_BASE), get_frame_rate(self.stream), 0) - else: - raise ValueError("Unable to determine number for frames") - - seek_frame = frame_count - - retry = 100 - - while retry: - target_sec = seek_frame * 1 / self.rate - target_pts = int(target_sec / self.time_base) + self.start_time - - self.stream.seek(int(target_pts)) - - frame_index = None - - for frame_index, frame in self.next_frame(): - print(frame_index, frame) - continue - - if not frame_index is None: - break - else: - seek_frame -= 1 - retry -= 1 - - - print("frame count seeked", frame_index, "container frame count", frame_count) - - return frame_index or frame_count - - @QtCore.pyqtSlot(object) - def set_file(self, path): - self.file = av.open(path) - self.stream = next(s for s in self.file.streams if s.type == b'video') - self.rate = get_frame_rate(self.stream) - self.time_base = float(self.stream.time_base) - - - index, first_frame = next(self.next_frame()) - self.stream.seek(self.stream.start_time) - - # find the pts of the first frame - index, first_frame = next(self.next_frame()) - - if self.pts_seen: - pts = first_frame.pts - else: - pts = first_frame.dts - - self.start_time = pts or first_frame.dts - - print("First pts", pts, self.stream.start_time, first_frame) - - #self.nb_frames = get_frame_count(self.file, self.stream) - self.nb_frames = self.get_frame_count() - - dur = None - - if self.stream.duration: - dur = self.stream.duration * self.time_base - else: - dur = self.file.duration * 1.0 / float(AV_TIME_BASE) - - self.update_frame_range.emit(dur, self.rate) - - - - - -class DisplayWidget(QtGui.QLabel): - def __init__(self, parent=None): - super().__init__(parent) - #self.setScaledContents(True) - self.setMinimumSize(1920 / 10, 1080 / 10) - - size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) - size_policy.setHeightForWidth(True) - - self.setSizePolicy(size_policy) - - self.setAlignment(Qt.AlignHCenter | Qt.AlignBottom) - - self.pixmap = None - self.setMargin(10) - - def heightForWidth(self, width): - return width * 9 / 16.0 - - @QtCore.pyqtSlot(object, object) - def setPixmap(self, img, index): - #if index == self.current_index: - self.pixmap = QtGui.QPixmap.fromImage(img) - - #super(DisplayWidget, self).setPixmap(self.pixmap) - super().setPixmap(self.pixmap.scaled(self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)) - - def sizeHint(self): - width = self.width() - return QtCore.QSize(width, self.heightForWidth(width)) - - def resizeEvent(self, event): - if self.pixmap: - super().setPixmap(self.pixmap.scaled(self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)) - - def sizeHint(self): - return QtCore.QSize(1920 / 2.5, 1080 / 2.5) - - -class VideoPlayerWidget(QtGui.QWidget): - - request_time = QtCore.pyqtSignal(object) - - load_file = QtCore.pyqtSignal(object) - - def __init__(self, parent=None): - super().__init__(parent) - - self.rate = None - - self.display = DisplayWidget() - self.timeline = QtGui.QScrollBar(Qt.Horizontal) - self.timeline_base = 100000 - - self.frame_grabber = FrameGrabber() - - self.frame_control = QtGui.QDoubleSpinBox() - self.frame_control.setFixedWidth(100) - - self.timeline.valueChanged.connect(self.slider_changed) - self.frame_control.valueChanged.connect(self.frame_changed) - - self.request_time.connect(self.frame_grabber.request_time) - self.load_file.connect(self.frame_grabber.set_file) - - self.frame_grabber.frame_ready.connect(self.display.setPixmap) - self.frame_grabber.update_frame_range.connect(self.set_frame_range) - - self.frame_grabber_thread = QtCore.QThread() - - self.frame_grabber.moveToThread(self.frame_grabber_thread) - self.frame_grabber_thread.start() - - control_layout = QtGui.QHBoxLayout() - control_layout.addWidget(self.frame_control) - control_layout.addWidget(self.timeline) - - layout = QtGui.QVBoxLayout() - layout.addWidget(self.display) - layout.addLayout(control_layout) - self.setLayout(layout) - self.setAcceptDrops(True) - - def set_file(self, path): - #self.frame_grabber.set_file(path) - self.load_file.emit(path) - self.frame_changed(0) - - @QtCore.pyqtSlot(object, object) - def set_frame_range(self, maximum, rate): - print("frame range =", maximum, rate, int(maximum * self.timeline_base)) - - self.timeline.setMaximum(int(maximum * self.timeline_base)) - - self.frame_control.setMaximum(maximum) - self.frame_control.setSingleStep(1 / rate) - #self.timeline.setSingleStep( int(AV_TIME_BASE * 1/rate)) - self.rate = rate - - def slider_changed(self, value): - print('..', value) - self.frame_changed(value * 1.0 / float(self.timeline_base)) - - def frame_changed(self, value): - self.timeline.blockSignals(True) - self.frame_control.blockSignals(True) - - self.timeline.setValue(int(value * self.timeline_base)) - self.frame_control.setValue(value) - - self.timeline.blockSignals(False) - self.frame_control.blockSignals(False) - - #self.display.current_index = value - self.frame_grabber.active_time = value - - self.request_time.emit(value) - - def keyPressEvent(self, event): - if event.key() in (Qt.Key_Right, Qt.Key_Left): - direction = 1 - if event.key() == Qt.Key_Left: - direction = -1 - - if event.modifiers() == Qt.ShiftModifier: - print('shift') - direction *= 10 - - direction = direction * 1 / self.rate - - self.frame_changed(self.frame_control.value() + direction) - - else: - super().keyPressEvent(event) - - def mousePressEvent(self, event): - # clear focus of spinbox - focused_widget = QtGui.QApplication.focusWidget() - if focused_widget: - focused_widget.clearFocus() - - super().mousePressEvent(event) - - def dragEnterEvent(self, event): - event.accept() - - def dropEvent(self, event): - - mime = event.mimeData() - event.accept() - - - if mime.hasUrls(): - path = str(mime.urls()[0].path()) - self.set_file(path) - def closeEvent(self, event): - - self.frame_grabber.active_time = -1 - self.frame_grabber_thread.quit() - self.frame_grabber_thread.wait() - - for key, value in sorted(self.frame_grabber.pts_map.items()): - print(key, '=', value) - - event.accept() - - -if __name__ == "__main__": - app = QtGui.QApplication(sys.argv) - window = VideoPlayerWidget() - test_file = sys.argv[1] - window.set_file(test_file) - window.show() - sys.exit(app.exec_())