From fe10f9812810b96e947499318e196f230dd60b18 Mon Sep 17 00:00:00 2001 From: kris Date: Thu, 21 Mar 2019 22:43:02 +0000 Subject: [PATCH] Support skipping frames from the input video to increase output image quality at the expense of frame rate. --- transcoder/main.py | 10 ++++++++-- transcoder/movie.py | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/transcoder/main.py b/transcoder/main.py index bdc3857..7a0516f 100644 --- a/transcoder/main.py +++ b/transcoder/main.py @@ -18,12 +18,18 @@ parser.add_argument( '--audio_normalization', type=float, default=None, help='Override auto-detected multiplier for audio normalization.' ) - +parser.add_argument( + '--every_n_video_frames', type=int, default=1, + help='Allows skipping frames of input video to lower effective output ' + 'frame rate, which may give better quality for some videos.' +) def main(args): filename = args.input m = movie.Movie( - filename, audio_normalization=args.audio_normalization) + filename, + every_n_video_frames=args.every_n_video_frames, + audio_normalization=args.audio_normalization) max_bytes_out = 1024. * 1024 * args.max_output_mb diff --git a/transcoder/movie.py b/transcoder/movie.py index 30e7276..2e270c9 100644 --- a/transcoder/movie.py +++ b/transcoder/movie.py @@ -9,16 +9,21 @@ import video class Movie: - def __init__(self, filename: str, audio_normalization: float = None): + def __init__( + self, filename: str, + every_n_video_frames: int = 1, + audio_normalization: float = None): self.filename = filename # type: str + self.every_n_video_frames = every_n_video_frames # type: int self.audio = audio.Audio( filename, normalization=audio_normalization) # type: audio.Audio self.video = video.Video(filename) # type: video.Video - self.cycles = 0 - self.stream_pos = 0 # type: int + # TODO: don't use this as well as cycle_counter, it's a relic of when + # I relied on variable-duration opcodes for frame timings. + self.cycles = 0 # type: int self.cycle_counter = machine.CycleCounter() self.state = machine.Machine( @@ -28,15 +33,21 @@ class Movie: ) def encode(self) -> Iterator[opcodes.Opcode]: + """ + + :return: + """ video_frames = self.video.frames() video_seq = None for au in self.audio.audio_stream(): self.cycles += self.audio.cycles_per_tick if self.video.tick(self.cycles): - print("Starting frame %d" % self.video.frame_number) video_frame = next(video_frames) - video_seq = self.video.encode_frame(video_frame) + if ((self.video.frame_number - 1) % self.every_n_video_frames + == 0): + print("Starting frame %d" % self.video.frame_number) + video_seq = self.video.encode_frame(video_frame) # au has range -15 .. 16 (step=1) # Tick cycles are units of 2 @@ -48,12 +59,21 @@ class Movie: yield opcodes.TICK_OPCODES[(tick, page)](content, offsets) def _emit_bytes(self, _op): - # print("%04X:" % self.stream_pos) + """ + + :param _op: + :return: + """ for b in self.state.emit(_op): yield b self.stream_pos += 1 def emit_stream(self, ops: Iterable[opcodes.Opcode]) -> Iterator[int]: + """ + + :param ops: + :return: + """ for op in ops: # Keep track of where we are in TCP client socket buffer socket_pos = self.stream_pos % 2048 @@ -63,5 +83,8 @@ class Movie: yield from self._emit_bytes(op) def done(self) -> Iterator[int]: - """Terminate opcode stream.""" + """Terminate opcode stream. + + :return: + """ yield from self._emit_bytes(opcodes.Terminate())