-
Notifications
You must be signed in to change notification settings - Fork 10
[codex] add video frame count method #195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -26,6 +26,8 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @runtime_checkable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class VideoSource(Protocol): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def get_frame_count(self) -> int: ... | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def clipped( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -94,6 +96,21 @@ def uri(self) -> str: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def open(self) -> IO[bytes]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self.data_file.open(mode="rb") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def get_frame_count(self) -> int: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from refiner.video.remux import prepare_video_source | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| prepared = await prepare_video_source(video=self) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if self.from_timestamp_s is not None or self.to_timestamp_s is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "encoded video frame count is unavailable for clipped videos" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if prepared.probe is None or prepared.probe.frame_count is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError(f"Video frame count is unavailable for {self.uri!r}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return prepared.probe.frame_count | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| prepared.close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def clipped( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -161,6 +178,22 @@ class VideoBytes: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def open(self) -> IO[bytes]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return io.BytesIO(self.data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def get_frame_count(self) -> int: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from refiner.video.remux import prepare_video_source | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| prepared = await prepare_video_source(video=self) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if self.from_timestamp_s is not None or self.to_timestamp_s is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "encoded video frame count is unavailable for clipped videos" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if prepared.probe is None or prepared.probe.frame_count is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source = self.uri or type(self).__name__ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError(f"Video frame count is unavailable for {source!r}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return prepared.probe.frame_count | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| prepared.close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+181
to
+195
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The check for clipped videos is performed after the expensive
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def clipped( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -250,6 +283,11 @@ def duration_s(self) -> float | None: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self.frame_count / float(self.fps) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def get_frame_count(self) -> int: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if self.frame_count is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self.frame_count | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return sum(1 for _ in self.iter_frame_arrays()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+286
to
+289
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using async def get_frame_count(self) -> int:
if self.frame_count is not None:
return self.frame_count
source = self.frames
frames = (
cast(Callable[[], Iterable[Any]], source)() if callable(source) else source
)
start_idx = (
0
if self.from_timestamp_s is None
else max(0, int(math.floor(float(self.from_timestamp_s) * self.fps)))
)
if isinstance(frames, Sequence) or hasattr(frames, "__len__"):
total_len = len(frames)
if self.to_timestamp_s is not None:
end_idx = max(
start_idx, int(math.ceil(float(self.to_timestamp_s) * self.fps))
)
end_idx = min(total_len, end_idx)
else:
end_idx = total_len
return max(0, end_idx - start_idx)
if self.to_timestamp_s is not None:
end_idx = max(
start_idx, int(math.ceil(float(self.to_timestamp_s) * self.fps))
)
else:
end_idx = None
count = 0
for index, _ in enumerate(frames):
if index < start_idx:
continue
if end_idx is not None and index >= end_idx:
break
count += 1
return count |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def iter_frame_arrays(self) -> Iterator[np.ndarray]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start_idx = ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -409,6 +447,9 @@ def frame_count(self) -> int: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def duration_s(self) -> float: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self.frame_count / float(self.fps) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def get_frame_count(self) -> int: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self.frame_count | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def iter_frame_arrays(self) -> Iterator[np.ndarray]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| yield from self._array | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check for clipped videos is performed after the expensive
prepare_video_sourcecall. Moving this check to the top of the method avoids unnecessary I/O and container opening overhead when the video is clipped.