Skip to content
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

--read-length CLI option + some code suggestions #11

Merged
merged 9 commits into from
Aug 16, 2023
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ See `ffmpeg-bitrate-stats -h`:

```
usage: __main__.py [-h] [-n] [-v] [-s {video,audio}] [-a {time,gop}]
[-c CHUNK_SIZE] [-of {json,csv}] [-p] [-pw PLOT_WIDTH]
[-ph PLOT_HEIGHT]
[-c CHUNK_SIZE] [-rs READ_START] [-rd READ_DURATION]
[-of {json,csv}] [-p] [-pw PLOT_WIDTH] [-ph PLOT_HEIGHT]
input

ffmpeg_bitrate_stats v0.4.3
ffmpeg_bitrate_stats v1.0.2

positional arguments:
input input file
Expand All @@ -69,6 +69,12 @@ options:
-c CHUNK_SIZE, --chunk-size CHUNK_SIZE
Custom aggregation window size in seconds (default:
1.0)
-rs READ_START, --read-start READ_START
Time to wait before sampling video (in HH:MM:SS.msec
or seconds) (default: None)
-rd READ_DURATION, --read-duration READ_DURATION
Duration for sampling stream (in HH:MM:SS.msec or
seconds) (default: None)
-of {json,csv}, --output-format {json,csv}
output in which format (default: json)
-p, --plot Plot the bitrate over time (to STDERR) (default:
Expand Down
17 changes: 17 additions & 0 deletions ffmpeg_bitrate_stats/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,21 @@ def main() -> None:
help="Custom aggregation window size in seconds",
)

parser.add_argument(
"-rs",
"--read-start",
type=str,
default=None,
help="Time to wait before sampling video (in HH:MM:SS.msec or seconds)",
)
parser.add_argument(
"-rd",
"--read-duration",
type=str,
default=None,
help="Duration for sampling stream (in HH:MM:SS.msec or seconds)",
)

parser.add_argument(
"-of",
"--output-format",
Expand Down Expand Up @@ -124,6 +139,8 @@ def main() -> None:
stream_type=cli_args.stream_type,
aggregation=cli_args.aggregation,
chunk_size=cli_args.chunk_size,
read_start=cli_args.read_start,
read_duration=cli_args.read_duration,
dry_run=cli_args.dry_run,
)
br.calculate_statistics()
Expand Down
40 changes: 34 additions & 6 deletions ffmpeg_bitrate_stats/bitrate_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@


def run_command(
cmd: List[str], dry_run: bool = False, verbose: bool = False
cmd: List[str], dry_run: bool = False
) -> tuple[str, str] | tuple[None, None]:
"""
Run a command directly
"""
if dry_run or verbose:

# for verbose mode
logger.debug("[cmd] " + " ".join(cmd))

if dry_run:
logger.info("[cmd] " + " ".join(cmd))
if dry_run:
return None, None
return None, None

process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
Expand Down Expand Up @@ -112,6 +115,8 @@ class BitrateStats:
stream_type (str, optional): Stream type (audio/video). Defaults to "video".
aggregation (str, optional): Aggregation type (time/gop). Defaults to "time".
chunk_size (int, optional): Chunk size. Defaults to 1.
read_start (str, optional): Start time for reading in HH:MM:SS.msec or seconds. Defaults to None.
read_duration (str, optional): Duration for reading in HH:MM:SS.msec or seconds. Defaults to None.
dry_run (bool, optional): Dry run. Defaults to False.
"""

Expand All @@ -121,6 +126,8 @@ def __init__(
stream_type: StreamType = "video",
aggregation: Aggregation = "time",
chunk_size: int = 1,
read_start: Optional[str] = None,
read_duration: Optional[str] = None,
dry_run: bool = False,
):
self.input_file = input_file
Expand All @@ -139,6 +146,11 @@ def __init__(
raise ValueError("Chunk size must be greater than 0")
self.chunk_size = chunk_size

self.read_length = "{}%{}".format(
f"+{read_start}" if read_start else "",
f"+{read_duration}" if read_duration else "",
)

self.dry_run = dry_run

self.duration: float = 0
Expand Down Expand Up @@ -191,6 +203,8 @@ def _calculate_frame_sizes(self) -> list[FrameEntry]:
"error",
"-select_streams",
self.stream_type[0] + ":0",
"-read_intervals",
self.read_length,
"-show_packets",
"-show_entries",
"packet=pts_time,dts_time,duration_time,size,flags",
Expand Down Expand Up @@ -250,6 +264,20 @@ def _calculate_frame_sizes(self) -> list[FrameEntry]:
if default_duration == "NaN":
ret = self._fix_durations(ret)

# fix missing data in first packet (occurs occassionally when reading streams)
if (
ret[0]["duration"] == "NaN"
and isinstance(ret[1]["duration"], float)
):
ret[0]["duration"] = ret[1]["duration"]

if (
ret[0]["pts"] == "NaN"
and isinstance(ret[1]["pts"], float)
and isinstance(ret[0]["duration"], float)
):
ret[0]["pts"] = ret[1]["pts"] - ret[0]["duration"]

self.frames = ret
return ret

Expand Down Expand Up @@ -281,8 +309,8 @@ def _calculate_duration(self) -> float:
Returns:
float: The duration in seconds.
"""
self.duration = round(
sum(f["duration"] for f in self.frames if f["duration"] != "NaN"), 2
self.duration = sum(
f["duration"] for f in self.frames if f["duration"] != "NaN"
)
return self.duration

Expand Down