reworked ffmpegg support

This commit is contained in:
q
2025-08-19 14:46:41 +03:00
parent 73cf3a3d76
commit 2a08b187c6
2 changed files with 50 additions and 37 deletions

View File

@@ -2,12 +2,21 @@ import argparse
from tsmark.video_annotator import Marker from tsmark.video_annotator import Marker
VERSION = "0.7.6" VERSION = "0.7.7"
class SmartFormatter(argparse.HelpFormatter):
"""Thanks to https://stackoverflow.com/users/1307905/anthon"""
def _split_lines(self, text, width):
if text.startswith("R|"):
return text[2:].splitlines()
return argparse.HelpFormatter._split_lines(self, text, width)
def get_options(): def get_options():
parser = argparse.ArgumentParser(description="Video timestamping tool") parser = argparse.ArgumentParser(description="Video timestamping tool", formatter_class=SmartFormatter)
parser.add_argument( parser.add_argument(
"--ts", "--ts",
action="store", action="store",
@@ -92,11 +101,17 @@ def get_options():
help="Print FFMPEG commands: Copy instead of recompress. If video is cropped, it must be recompressed", help="Print FFMPEG commands: Copy instead of recompress. If video is cropped, it must be recompressed",
) )
parser.add_argument( parser.add_argument(
"--ffmpeg-an", "--ffmpeg-args",
action="store_true", action="store",
default=False, default="-i {input} {crop} -c:v mpeg2video -q:v 3 -g 1 -c:a libmp3lame -ar 44100 -b:a 192k -ss {start_time} -to {end_time} {output}.mpeg",
required=False, required=False,
help="Print FFMPEG commands: Discard audio track", help="""R|FFMPEG arguments.
Default: '%(default)s'
Note: {output} is without extension. -i and -ss should switch, depending on input and output formats.
Other useful arguments:
'-i {input} -c:v copy -an -ss {start_time} -to {end_time} {output}.mpeg'
'-ss {start_time} -to {end_time} -i {input} {crop} -g 250 -c:v libx264 -crf 23 -c:a aac -movflags faststart -strict -2 -y {output}.mp4'
""",
) )
parser.add_argument( parser.add_argument(
"--ffmpeg-run", "--ffmpeg-run",

View File

@@ -11,12 +11,6 @@ import cv2
import numpy as np import numpy as np
from scipy.interpolate import PchipInterpolator from scipy.interpolate import PchipInterpolator
AUDIO_COMPRESS = "-c:a libmp3lame -ar 44100 -b:a 192k".split(" ")
AUDIO_COPY = "-c:a copy".split(" ")
AUDIO_DISCARD = "-an".split(" ")
VIDEO_COMPRESS = "-c:v mpeg2video -q:v 3 -g 1".split(" ")
VIDEO_COPY = "-c:v copy".split(" ")
EXT_COMPRESS = ".mpeg"
PLUGIN_FOLDER = os.path.expanduser("~/.config/tsmark/plugins") PLUGIN_FOLDER = os.path.expanduser("~/.config/tsmark/plugins")
COLOR_PREPOST = (0, 128, 128) COLOR_PREPOST = (0, 128, 128)
@@ -988,19 +982,14 @@ class World:
cropstr = ["-vf", f"crop={w}:{h}:{x}:{y}"] cropstr = ["-vf", f"crop={w}:{h}:{x}:{y}"]
self.stamps.sort() self.stamps.sort()
print("# Timestamps:") print("# Timestamps:")
audio_str = AUDIO_COPY if self.opts.ffmpeg_copy else AUDIO_COMPRESS
video_str = VIDEO_COPY if self.opts.ffmpeg_copy else VIDEO_COMPRESS
audio_str = AUDIO_DISCARD if self.opts.ffmpeg_an else audio_str
for i, ts in enumerate(self.stamps): for i, ts in enumerate(self.stamps):
print("# {}: {} / {}".format(i + 1, self.format_time(ts), ts)) print("# {}: {} / {}".format(i + 1, self.format_time(ts), ts))
if len(self.stamps) == 0: if len(self.stamps) == 0:
self.stamps.append(0) self.stamps.append(0)
self.stamps.append(self.frames) self.stamps.append(self.frames)
padlen = len(str(self.frames)) padlen = len(str(self.frames))
src_name = self.opts.video.replace('"', '\\"') src_name_print = self.opts.video.replace('"', '\\"')
tgt_name = os.path.splitext(self.opts.video)[0].replace('"', '\\"') tgt_name_print = os.path.splitext(self.opts.video)[0].replace('"', '\\"')
tgt_ext = os.path.splitext(self.opts.video)[1] if self.opts.ffmpeg_copy else EXT_COMPRESS
for i in range(1, len(self.stamps), 2): for i in range(1, len(self.stamps), 2):
from_ts = self.stamps[i - 1] from_ts = self.stamps[i - 1]
@@ -1009,25 +998,34 @@ class World:
to_ft = self.format_time(to_ts) to_ft = self.format_time(to_ts)
from_str = str(from_ts).zfill(padlen) from_str = str(from_ts).zfill(padlen)
to_str = str(to_ts).zfill(padlen) to_str = str(to_ts).zfill(padlen)
ffmpeg_cmd = [
"ffmpeg", ffmpeg_args_print = []
"-hide_banner", ffmpeg_args = []
"-i",
shlex.quote(src_name), for arg in shlex.split(self.opts.ffmpeg_args):
*cropstr, if arg == "{crop}":
*video_str, ffmpeg_args_print.extend(cropstr)
*audio_str, ffmpeg_args.extend(cropstr)
"-ss", else:
f"{from_ft}", ffmpeg_args_print.append(
"-to", arg.format(
f"{to_ft}", input=shlex.quote(src_name_print),
shlex.quote(f"{tgt_name}.trim.{from_str}-{to_str}{tgt_ext}"), output=shlex.quote(f"{tgt_name_print}.trim.{from_str}-{to_str}"),
] start_time=from_ft,
print(" ".join(ffmpeg_cmd)) end_time=to_ft,
)
)
ffmpeg_args.append(
arg.format(
input=self.opts.video,
output=f"{os.path.splitext(self.opts.video)[0]}.trim.{from_str}-{to_str}",
start_time=from_ft,
end_time=to_ft,
)
)
print(" ".join(["ffmpeg", "-hide_banner", *ffmpeg_args_print]))
if self.opts.ffmpeg_run: if self.opts.ffmpeg_run:
ffmpeg_cmd[3] = src_name subprocess.run(["ffmpeg", "-hide_banner", *ffmpeg_args])
ffmpeg_cmd[-1] = f"{tgt_name}.trim.{from_str}-{to_str}{tgt_ext}"
subprocess.run(ffmpeg_cmd)
def save_timestamps(self): def save_timestamps(self):