def _split(self, file_path): """Split videos using ffmpeg library first by copying audio and video codecs from input files, it leads to faster splitting, But if resulting splitted videos are unreadable try again splitting by using ffmpeg default codecs. If splitteed videos are still unreadable throw an exception. :param file_path: path of video file :type file_path: str, required :return: List of path of splitted video clips :rtype: list """ chunked_videos = self._split_with_ffmpeg(file_path) corruption_in_chunked_videos = False for chunked_video in chunked_videos: if not helper._check_if_valid_video(chunked_video): corruption_in_chunked_videos = True if corruption_in_chunked_videos: chunked_videos = self._split_with_ffmpeg(file_path, override_video_codec=True) for chunked_video in chunked_videos: if not helper._check_if_valid_video(chunked_video): raise Exception( "Error in splitting videos in multiple chunks, corrupted video chunk: " + chunked_video) return chunked_videos
def extract_keyframes_from_videos_dir(self, no_of_frames, dir_path): """Returns best key images/frames from the videos in the given directory. you need to mention number of keyframes as well as directory path containing videos. Function returns python dictionary with key as filepath each dictionary element contains list of python numpy image objects as keyframes. :param no_of_frames: Number of key frames to be extracted :type no_of_frames: int, required :param dir_path: Directory location with videos :type dir_path: str, required :return: Dictionary with key as filepath and numpy.2darray Image objects :rtype: dict """ all_videos_top_frames = {} for path, subdirs, files in os.walk(dir_path): for filename in files: filepath = os.path.join(path, filename) if helper._check_if_valid_video(filepath): video_top_frames = self.extract_video_keyframes( no_of_frames, filepath) all_videos_top_frames[filepath] = video_top_frames return all_videos_top_frames
def compress_videos_from_dir( self, dir_path, force_overwrite=False, crf_parameter=config.Video.video_compression_crf_parameter, output_video_codec=config.Video.video_compression_codec, out_dir_path="", out_file_name="", ): """Function to compress input video files in a folder :param dir_path: Input folder path :type dir_path: str :param force_overwrite: optional parameter if True then if there is \ already a file in output file location function will overwrite it, defaults to False :type force_overwrite: bool, optional :param crf_parameter: Constant Rate Factor Parameter for controlling \ amount of video compression to be applied, The range of the quantizer scale is 0-51:\ where 0 is lossless, 23 is default, and 51 is worst possible.\ It is recommend to keep this value between 20 to 30 \ A lower value is a higher quality, you can change default value by changing \ config.Video.video_compression_crf_parameter :type crf_parameter: int, optional :param output_video_codec: Type of video codec to choose, \ Currently supported options are libx264 and libx265, libx264 is default option.\ libx264 is more widely supported on different operating systems and platforms, \ libx265 uses more advanced x265 codec and results in better compression and even less \ output video sizes with same or better quality. Right now libx265 is not as widely compatible \ on older versions of MacOS and Widows by default. If wider video compatibility is your goal \ you should use libx264., you can change default value by changing Katna.config.Video.video_compression_codec :type output_video_codec: str, optional :param out_dir_path: output folder path where you want output video to be saved, defaults to "" :type out_dir_path: str, optional :raises Exception: raises FileNotFoundError Exception if input video file not found, also exception is raised in case output video file path already exist and force_overwrite is not set to True. :return: Status code Returns True if video compression was successfull else False :rtype: bool """ status = True list_of_videos_to_process = [] # Collect all the valid video files inside folder for path, _, files in os.walk(dir_path): for filename in files: video_file_path = os.path.join(path, filename) if helper._check_if_valid_video(video_file_path): list_of_videos_to_process.append(video_file_path) # Need to run in two sepearte loops to prevent recursion for video_file_path in list_of_videos_to_process: statusI = self.compress_video( video_file_path, force_overwrite=force_overwrite, crf_parameter=crf_parameter, output_video_codec=output_video_codec, out_dir_path=out_dir_path, ) status = bool(status and statusI) return status
def compress_video( self, file_path, force_overwrite=False, crf_parameter=config.Video.video_compression_crf_parameter, output_video_codec=config.Video.video_compression_codec, out_dir_path="", out_file_name="", ): """Function to compress given input file :param file_path: Input file path :type file_path: str :param force_overwrite: optional parameter if True then if there is \ already a file in output file location function will overwrite it, defaults to False :type force_overwrite: bool, optional :param crf_parameter: Constant Rate Factor Parameter for controlling \ amount of video compression to be applied, The range of the quantizer scale is 0-51:\ where 0 is lossless, 23 is default, and 51 is worst possible.\ It is recommend to keep this value between 20 to 30 \ A lower value is a higher quality, you can change default value by changing \ config.Video.video_compression_crf_parameter :type crf_parameter: int, optional :param output_video_codec: Type of video codec to choose, \ Currently supported options are libx264 and libx265, libx264 is default option.\ libx264 is more widely supported on different operating systems and platforms, \ libx265 uses more advanced x265 codec and results in better compression and even less \ output video sizes with same or better quality. Right now libx265 is not as widely compatible \ on older versions of MacOS and Widows by default. If wider video compatibility is your goal \ you should use libx264., you can change default value by changing \ Katna.config.Video.video_compression_codec :type output_video_codec: str, optional :param out_dir_path: output folder path where you want output video to be saved, defaults to "" :type out_dir_path: str, optional :param out_file_name: output filename, if not mentioned it will be same as input filename, defaults to "" :type out_file_name: str, optional :raises Exception: raises FileNotFoundError Exception if input video file not found, also exception is raised in case output video file path already exist and force_overwrite is not set to True. :return: Status code Returns True if video compression was successfull else False :rtype: bool """ # TODO add docstring for exeception # Add details where libx265 will make sense if not helper._check_if_valid_video(file_path): raise Exception("Invalid or corrupted video: " + file_path) # Intialize video compression class video_compressor = VideoCompressor() # Run video compression status = video_compressor.compress_video( file_path, force_overwrite, crf_parameter, output_video_codec, out_dir_path, out_file_name, ) return status
def resize_video_from_dir(self, dir_path, abs_dir_path_output, aspect_ratio): """Resize all videos inside the directory :param dir_path: Directory path where videos are located :type dir_path: str :param abs_dir_path_output: Absolute path to directory where output videos should to be dumped :type abs_dir_path_output: str :param aspect_ratio: desirable aspect ratio for the videos :type aspect_ratio: [type] :raises Exception: [description] """ if self.mediapipe_autoflip is None: raise Exception("Mediapipe build path not found.") # prepare the mediapipe autoflip pipeline self.mediapipe_autoflip.prepare_pipeline() # make the output dir if it doesn't exist if not os.path.isdir(abs_dir_path_output): os.mkdir(abs_dir_path_output) list_of_videos_to_process = [] # Collect all the valid video files inside folder for path, _, files in os.walk(dir_path): for filename in files: video_file_path = os.path.join(path, filename) if helper._check_if_valid_video(video_file_path): list_of_videos_to_process.append(video_file_path) # autoflip = self.mediapipe_autoflip # generates a pool based on cores pool = Pool(processes=self.n_processes) print("This might take a while ... ") try: results = pool.starmap( self.mediapipe_autoflip.run, [( input_file_path, os.path.join(abs_dir_path_output, ntpath.basename(input_file_path)), aspect_ratio, ) for input_file_path in list_of_videos_to_process], ) pool.close() pool.join() except Exception as e: self.mediapipe_autoflip.exit_clean() raise e self.mediapipe_autoflip.exit_clean() print("Finished processing for files")
def extract_video_keyframes(self, no_of_frames, file_path): """Returns a list of best key images/frames from a single video. :param no_of_frames: Number of key frames to be extracted :type no_of_frames: int, required :param file_path: video file location :type file_path: str, required :return: List of numpy.2darray Image objects :rtype: list """ # Creating the multiprocessing pool self.pool_extractor = Pool(processes=self.n_processes) self.pool_selector = Pool(processes=self.n_processes) # Split the input video into chunks. Each split(video) will be stored # in a temp if not helper._check_if_valid_video(file_path): raise Exception("Invalid or corrupted video: " + file_path) # split videos in chunks in smaller chunks for parallel processing. chunked_videos = self._split(file_path) frame_extractor = FrameExtractor() # Passing all the clipped videos for the frame extraction using map function of the # multiprocessing pool with self.pool_extractor: extracted_candidate_frames = self.pool_extractor.map( frame_extractor.extract_candidate_frames, chunked_videos) # Converting the nested list of extracted frames into 1D list extracted_candidate_frames = [ frame for frames in extracted_candidate_frames for frame in frames ] self._remove_clips(chunked_videos) image_selector = ImageSelector(self.pool_selector) top_frames = image_selector.select_best_frames( extracted_candidate_frames, no_of_frames) return top_frames