Skip to content

split_video_ffmpeg lib function update #363

Closed

Description

@Deepchavda007

Description:
split_video_ffmpeg inj this function we can't save specific output dir.

Example:

from scenedetect import detect, ContentDetector, split_video_ffmpeg
scene_list = detect('my_video.mp4', ContentDetector())
split_video_ffmpeg('my_video.mp4', scene_list)

Now we can save specific output dir.

from scenedetect import detect, ContentDetector, split_video_ffmpeg
scene_list = detect('my_video.mp4', ContentDetector())
split_video_ffmpeg(video_path, scene_list, output_dir=video_clips_dir)

video_splitter.py in this file update this function:

def split_video_ffmpeg(
    input_video_path: str,
    scene_list: Iterable[TimecodePair],
    output_file_template: str = '$VIDEO_NAME-Scene-$SCENE_NUMBER.mp4',
    video_name: Optional[str] = None,
    arg_override: str = DEFAULT_FFMPEG_ARGS,
    show_progress: bool = False,
    show_output: bool = False,
    suppress_output=None,
    hide_progress=None,
    output_dir: Optional[str] = None
):
    """ Calls the ffmpeg command on the input video, generating a new video for
    each scene based on the start/end timecodes.

    Arguments:
        input_video_path: Path to the video to be split.
        scene_list (List[Tuple[FrameTimecode, FrameTimecode]]): List of scenes
            (pairs of FrameTimecodes) denoting the start/end frames of each scene.
        output_file_template (str): Template to use for generating the output filenames.
            Can use $VIDEO_NAME and $SCENE_NUMBER in this format, for example:
            `$VIDEO_NAME - Scene $SCENE_NUMBER.mp4`
        video_name (str): Name of the video to be substituted in output_file_template. If not
            passed will be calculated from input_video_path automatically.
        arg_override (str): Allows overriding the arguments passed to ffmpeg for encoding.
        show_progress (bool): If True, will show progress bar provided by tqdm (if installed).
        show_output (bool): If True, will show output from ffmpeg for first split.
        suppress_output: [DEPRECATED] DO NOT USE. For backwards compatibility only.
        hide_progress: [DEPRECATED] DO NOT USE. For backwards compatibility only.

    Returns:
        Return code of invoking ffmpeg (0 on success). If scene_list is empty, will
        still return 0, but no commands will be invoked.
    """
    # Handle backwards compatibility with v0.5 API.
    if isinstance(input_video_path, list):
        logger.error('Using a list of paths is deprecated. Pass a single path instead.')
        if len(input_video_path) > 1:
            raise ValueError('Concatenating multiple input videos is not supported.')
        input_video_path = input_video_path[0]
    if suppress_output is not None:
        logger.error('suppress_output is deprecated, use show_output instead.')
        show_output = not suppress_output
    if hide_progress is not None:
        logger.error('hide_progress is deprecated, use show_progress instead.')
        show_progress = not hide_progress

    if not scene_list:
        return 0

    logger.info('Splitting input video using ffmpeg, output path template:\n  %s',
                output_file_template)

    if video_name is None:
        video_name = get_file_name(input_video_path, include_extension=False)

    # Ensure the output directory exists
    if output_dir is not None and not os.path.exists(output_dir):
        os.makedirs(output_dir)

    arg_override = arg_override.replace('\\"', '"')

    ret_val = 0
    arg_override = arg_override.split(' ')
    scene_num_format = '%0'
    scene_num_format += str(max(3, math.floor(math.log(len(scene_list), 10)) + 1)) + 'd'

    try:
        progress_bar = None
        total_frames = scene_list[-1][1].get_frames() - scene_list[0][0].get_frames()
        if show_progress:
            progress_bar = tqdm(total=total_frames, unit='frame', miniters=1, dynamic_ncols=True)
        processing_start_time = time.time()
        for i, (start_time, end_time) in enumerate(scene_list):
            duration = (end_time - start_time)
            # Format output filename with template variable
            output_filename  = Template(output_file_template).safe_substitute(
                VIDEO_NAME=video_name,
                SCENE_NUMBER=scene_num_format % (i + 1),
                START_TIME=str(start_time.get_timecode().replace(":", ";")),
                END_TIME=str(end_time.get_timecode().replace(":", ";")),
                START_FRAME=str(start_time.get_frames()),
                END_FRAME=str(end_time.get_frames()))
            
            if output_dir is not None:
                output_filepath = os.path.join(output_dir, output_filename)
            else:
                output_filepath = output_filename

            # Gracefully handle case where FFMPEG_PATH might be unset.
            call_list = [FFMPEG_PATH if FFMPEG_PATH is not None else 'ffmpeg']
            if not show_output:
                call_list += ['-v', 'quiet']
            elif i > 0:
                # Only show ffmpeg output for the first call, which will display any
                # errors if it fails, and then break the loop. We only show error messages
                # for the remaining calls.
                call_list += ['-v', 'error']
            call_list += [
                '-nostdin', '-y', '-ss',
                str(start_time.get_seconds()), '-i', input_video_path, '-t',
                str(duration.get_seconds())
            ]
            call_list += arg_override
            call_list += ['-sn']
            # call_list += [output_file_template_iter]
            call_list += [output_filepath]
            ret_val = invoke_command(call_list)
            if show_output and i == 0 and len(scene_list) > 1:
                logger.info(
                    'Output from ffmpeg for Scene 1 shown above, splitting remaining scenes...')
            if ret_val != 0:
                # TODO(v0.6.2): Capture stdout/stderr and display it on any failed calls.
                logger.error('Error splitting video (ffmpeg returned %d).', ret_val)
                break
            if progress_bar:
                progress_bar.update(duration.get_frames())

        if progress_bar:
            progress_bar.close()
        if show_output:
            logger.info('Average processing speed %.2f frames/sec.',
                        float(total_frames) / (time.time() - processing_start_time))

    except CommandTooLong:
        logger.error(COMMAND_TOO_LONG_STRING)
    except OSError:
        logger.error('ffmpeg could not be found on the system.'
                     ' Please install ffmpeg to enable video output support.')
    return ret_val

Activity

  1. Breakthrough commented on Dec 1, 2023

    @Breakthrough
    Owner

    Originally I had closed this issue as part of #298, but this has come up enough now that we should probably add support for this. I'll close this issue as a duplicate and re-open #298 for the following release. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions