diff --git a/moviepy/video/io/ffmpeg_reader.py b/moviepy/video/io/ffmpeg_reader.py index 926ec3be9..6eb651039 100644 --- a/moviepy/video/io/ffmpeg_reader.py +++ b/moviepy/video/io/ffmpeg_reader.py @@ -341,8 +341,9 @@ def _reset_state(self): # should be ignored self._inside_output = False - # flag which indicates that a default stream has not been found yet - self._default_stream_found = False + # map from stream type to default stream + # if a default stream is not indicated, pick the first one available + self._default_streams = {} # current input file, stream and chapter, which will be built at runtime self._current_input_file = {"streams": []} @@ -422,16 +423,13 @@ def parse(self): "stream_number": stream_number, "stream_type": stream_type_lower, "language": language if language != "und" else None, - "default": not self._default_stream_found - or line.endswith("(default)"), + "default": (stream_type_lower not in self._default_streams) and line.endswith("(default)"), } - self._default_stream_found = True # for default streams, set their numbers globally, so it's # easy to get without iterating all if self._current_stream["default"]: - result[f"default_{stream_type_lower}_input_number"] = input_number - result[f"default_{stream_type_lower}_stream_number"] = stream_number + self._default_streams[stream_type_lower] = self._current_stream # exit chapter if self._current_chapter: @@ -456,7 +454,7 @@ def parse(self): # parse relevant data by stream type try: - global_data, stream_data = self.parse_data_by_stream_type( + stream_data = self.parse_data_by_stream_type( stream_type, line ) except NotImplementedError as exc: @@ -464,7 +462,6 @@ def parse(self): f"{str(exc)}\nffmpeg output:\n\n{self.infos}", UserWarning ) else: - result.update(global_data) self._current_stream.update(stream_data) elif line.startswith(" Metadata:"): # enter group " Metadata:" @@ -522,6 +519,27 @@ def parse(self): ] result["inputs"].append(self._current_input_file) + # set any missing default automatically + for stream in self._current_input_file["streams"]: + if stream["stream_type"] not in self._default_streams: + self._default_streams[stream["stream_type"]] = stream + stream["default"] = True + + # set some global info based on the defaults + for stream_type_lower, stream_data in self._default_streams.items(): + result[f"default_{stream_type_lower}_input_number"] = stream_data["input_number"] + result[f"default_{stream_type_lower}_stream_number"] = stream_data["stream_number"] + + if stream_type_lower == "audio": + result["audio_found"] = True + result["audio_fps"] = stream_data["fps"] + result["audio_bitrate"] = stream_data["bitrate"] + elif stream_type_lower == "video": + result["video_found"] = True + result["video_size"] = stream_data.get("size", None) + result["video_bitrate"] = stream_data.get("bitrate", None) + result["video_fps"] = stream_data["fps"] + # some video duration utilities if result["video_found"] and self.check_duration: result["video_n_frames"] = int(result["duration"] * result["video_fps"]) @@ -543,7 +561,7 @@ def parse_data_by_stream_type(self, stream_type, line): return { "Audio": self.parse_audio_stream_data, "Video": self.parse_video_stream_data, - "Data": lambda _line: ({}, {}), + "Data": lambda _line: {}, }[stream_type](line) except KeyError: raise NotImplementedError( @@ -553,7 +571,7 @@ def parse_data_by_stream_type(self, stream_type, line): def parse_audio_stream_data(self, line): """Parses data from "Stream ... Audio" line.""" - global_data, stream_data = ({"audio_found": True}, {}) + stream_data = {} try: stream_data["fps"] = int(re.search(r" (\d+) Hz", line).group(1)) except (AttributeError, ValueError): @@ -564,14 +582,11 @@ def parse_audio_stream_data(self, line): stream_data["bitrate"] = ( int(match_audio_bitrate.group(1)) if match_audio_bitrate else None ) - if self._current_stream["default"]: - global_data["audio_fps"] = stream_data["fps"] - global_data["audio_bitrate"] = stream_data["bitrate"] - return (global_data, stream_data) + return stream_data def parse_video_stream_data(self, line): """Parses data from "Stream ... Video" line.""" - global_data, stream_data = ({"video_found": True}, {}) + stream_data = {} try: match_video_size = re.search(r" (\d+)x(\d+)[,\s]", line) @@ -623,11 +638,7 @@ def parse_video_stream_data(self, line): fps = x * coef stream_data["fps"] = fps - if self._current_stream["default"]: - global_data["video_size"] = stream_data.get("size", None) - global_data["video_bitrate"] = stream_data.get("bitrate", None) - global_data["video_fps"] = stream_data["fps"] - return (global_data, stream_data) + return stream_data def parse_fps(self, line): return float(re.search(r" (\d+.?\d*) fps", line).group(1))