1. Home
  2. 데이터 과학/ChatGPT
  3. [게임해도 100% 안도는 CPU를 100%로 갈궈보자!] Python 이용하여 다량의 mp4 파일 gif, webp 로 한꺼번에 변환하기 feat. ChatGPT

[게임해도 100% 안도는 CPU를 100%로 갈궈보자!] Python 이용하여 다량의 mp4 파일 gif, webp 로 한꺼번에 변환하기 feat. ChatGPT

저번 인왕2 플레이 근황글을 적으면서 Nvidia Highlights 가 녹화한 하이라이트 영상(*.mp4) 을 움짤포맷(*.gif / webp) 으로 바꿀일이 생겼습니다.

 

지금 인왕2를 켜놓고 글을 작성하고 있는데 이래도 CPU는 100%로 돌지 않습니다. 

현대 CPU에게 게임 1개 + 멀티 태스킹 정도야 가볍죠~ 참고로 제가 쓰는 CPU는 라이젠 5600X입니다 ㅎㅎ.

 

일단 본론으로 돌아와서.. mp4를 gif 나 webp로 가장 가성비 있고 빠르게 바꾸는 방법은 FFmpeg를 사용하는 방법입니다. 웹에 있는 변환기는 일단 느려터지기도 했고 정보를 수집한다는 점에서 (내 영상 파일을 웹으로 업로드 해야함) 좀 찝찝하기도 합니다.

 

FFmpeg 는 오픈소스 프로젝트로 모든 동영상, 음악, 사진 포맷들의 디코딩과 인코딩을 목표로 수행되는 프로젝트입니다!

이 프로그램만 있으면 영상을 쉽게 특정 해상도로 재인코딩 하거나 포맷을 바꿀 수 있습니다.

 

이번에는 mp4 영상을 webp나 gif 포맷으로 바꿔야 하므로 ffmpeg 를 사용해 볼겁니다.

ffmpeg 의 사용방법은 아주 간단한데 ffmpeg 를 github에서 다운로드 받은 다음에 ffmpeg.exe를 실행 시, 영상 경로(mp4 경로) 와 변환 옵션 등을 명시해주고 실행해주면 됩니다.

 

물론 지금의 경우 다량의 mp4 파일을 한꺼번에 gif / webp로 바꿔야 하는 상황입니다..

이건 어떻게 해야할까요?

 

간단하게도 그냥 영상에 맞춰서 ffmpeg.exe를 여러번 실행시켜주면 됩니다. ffmpeg 자체가 그냥 실행 파일(*.exe) 이라서요... 우리가 쓰는 운영체제는 같은 프로그램을 여러번 실행시킬 수 있죠 ㅎㅎ 메모장 프로그램을 5개도 띄울 수 있고 10개도 띄울 수 있습니다. ffmpeg 도 그렇게 해서 여러개 실행시켜서 우리의 CPU를 한번 100% 까지 갈궈봅시다!

 

Python Script

지금 본 프로젝트의 목적은 AppData\Local\Temp\Highlights\Nioh 2 The Complete Edition 안에 저장되어 있는 Nvidia Highlight 가 녹화한 모아둔 mp4 파일을 모두 한꺼번에 gif / webp로 바꾸는 작업입니다.

 

mp4 파일에 맞춰서 각각 ffmpeg.exe 를 실행시켜 줘야 하는데 윈도우 Batch 파일로도 할 수 있으나 저는 프로그래밍 언어 Python 의 도움을 받아보겠습니다.

 

Python으로 프로그래밍 하여 AppData\Local\Temp\Highlights\Nioh 2 The Complete Edition 안에 있는 모든 *.mp4 파일을 찾아서 ffmpeg 로 한꺼번에 gif / webp 로 변환시키도록 하겠습니다.

 

단 코드 짜는게 귀찮으니깐 ChatGPT한테 시켜서 코드를 짜봅시다 ㅎㅎ.

 

아래는 폴더에서 모든 mp4 파일을 찾아서 각각 그에 맞게 ffmpeg.exe 를 실행시켜서 gif / webp 로 변환시키는 파이썬 코드 예시입니다.

 

MP4 to GIF

# MP4 -> GIF 일괄 변환

import os
import subprocess
from concurrent.futures import ProcessPoolExecutor

# ffmpeg_path = r"C:\ffmpeg\bin\ffmpeg.exe"
ffmpeg_path = "ffmpeg"
source_folder = r"원본 경로"
destination_folder = r"저장 경로"

# 용량에 큰 영향을 끼치는 값 = fps
width = 800
height = 400
fps = 10


def convert_to_gif(file_path):
    output_path = os.path.join(
        destination_folder, os.path.relpath(file_path, source_folder))
    output_path = os.path.splitext(output_path)[0] + ".gif"
    command = [ffmpeg_path, "-i", file_path, "-vf",
               f"fps={fps},scale={width}:{height}:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse", "-loop", "0", output_path]
    # command = [ffmpeg_path, "-i", file_path, "-vf", f"fps={fps},scale={width}:{height}:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle", "-loop", "0", "-f", "gif", "- | gifsicle --optimize=3 --delay=10 > " + output_path]
    subprocess.run(command, check=True)


if __name__ == "__main__":
    with ProcessPoolExecutor() as executor:
        for root, _, files in os.walk(source_folder):
            for file in files:
                if file.endswith(".mp4"):
                    file_path = os.path.join(root, file)
                    executor.submit(convert_to_gif, file_path)

    print("Task Finished :D")

 

MP4 to WebP

# MP4 -> webp 일괄 변환

import os
import subprocess
from concurrent.futures import ProcessPoolExecutor

# ffmpeg_path = r"C:\ffmpeg\bin\ffmpeg.exe"
ffmpeg_path = "ffmpeg"
source_folder = r"원본 경로"
destination_folder = r"저장 경로"

width = 800
height = 400
fps = 10
quality = 50  # default : 75


def convert_to_webp(file_path):
    output_path = os.path.join(
        destination_folder, os.path.relpath(file_path, source_folder))
    output_path = os.path.splitext(output_path)[0] + ".webp"
    command = [ffmpeg_path, "-i", file_path, "-vcodec", "libwebp", f"-quality {quality}", "-filter:v", f"fps=fps={fps}",
               "-lossless", "1", "-loop", "0", "-preset", "default", "-an", "-vsync", "0", "-s", f"{width}:{height}", output_path]
    subprocess.run(command, check=True)


if __name__ == "__main__":
    with ProcessPoolExecutor() as executor:
        for root, _, files in os.walk(source_folder):
            for file in files:
                if file.endswith(".mp4"):
                    file_path = os.path.join(root, file)
                    executor.submit(convert_to_webp, file_path)
    print("Task Finished :D")

위 두 코드는 ChatGPT 형님이 짜주신 mp4 -> webp, gif 전체 변환 Python 코드입니다.

source_folder 엔 mp4 가 위치하는 경로 위치,

destination_folder은 출력 결과물(webp / gif) 를 저장할 폴더 위치를 정합니다.

 

그리고 ffmpeg_path 에서 ffmpeg.exe 파일 위치를 정하는데 저는 이미 환경 변수에 ffmpeg를 등록해놔서 전역적으로 사용이 가능하기 때문에 그냥 ffmpeg 라고 적었습니다. 만약에 ffmpeg 를 따로 받으셔서 환경변수에 등록하지 않은 분들은 받은 ffmpeg.exe 절대 경로를 입력해주시면 됩니다.

 

이외에도 변환할 움짤의 크기 (width, height) 랑 퀄리티, fps 등을 조정해주시면 됩니다..

참고로 webp는 압축률이 높아서 상관없는데 연식이 오래된 gif 확장자의 경우 용량을 줄이고 싶으시면 움짤 크기보단 fps를 줄이는걸 적극 권장드립니다.. 제가 저기서 변환하려는 인왕2 하이라이트 mp4 파일의 경우 개당 파일이 약 200MB ~ 250MB인데 gif 로 해상도 낮춰서 변환을 해도 100MB가 넘어갑니다 ㅠㅠ

 

티스토리 업로드 제한은 30MB인데.. fps를 10정도로 조절하면 움짤 파일 크기가 극적으로 줄어듭니다.

어쨌던 위 코드에서 바꿀 변수값만 적당히 수정하고 실행시켜주면..?

 

참고로 티스토리에서 webp 업로드를 막아놨기 때문에 저는 webp 대신에 어쩔 수 없이 gif 로 변환하도록 하겠습니다.

 

게임 돌려도 100%를 못찍던 CPU가 모든 코어(쓰레드) 를 갈구면서 그래프를 꽉꽉 채우는 아름다운 모습을 보여줍니다.

현대 시분할 운영체제 Context Switching의 아름다움

어쨌던 기다리면 이렇게 폴더에 gif 로 변환된 파일이 나오는걸 보실 수 있습니다 ㅎ

참고로 compressed 의 경우 제가 gifsicle 이란 프로그램을 이용하여 한번 더 압축했습니다.

(gifsicle 은 여기서 구할 수 있습니다. 또는 윈도우 패키지 매니저 choco 를 이용하여 설치하셔두 됨.)

 

import os
import subprocess
from concurrent.futures import ProcessPoolExecutor

# ffmpeg_path = r"C:\ffmpeg\bin\ffmpeg.exe"
gifsicle_path = "gifsicle"
source_folder = r"source"
destination_folder = r"dest"

# 용량에 큰 영향을 끼치는 값 = fps
width = "800"
height = "400"
fps = "10"

def convert_to_gif(file_path):
    output_path = os.path.join(destination_folder, os.path.relpath(file_path, source_folder))
    output_path = os.path.splitext(output_path)[0] + "_compressed" + ".gif"
    command = [gifsicle_path, "-O3", "--lossy=80", file_path, "-o", output_path]
    subprocess.run(command, check=True)

if __name__ == "__main__":
    with ProcessPoolExecutor() as executor:
        for root, _, files in os.walk(source_folder):
            for file in files:
                if file.endswith(".gif"):
                    file_path = os.path.join(root, file)
                    executor.submit(convert_to_gif, file_path)

    print("Task Finished :D")

참고로 gifsicle 을 이용하여 모든 폴더에 있는 gif 파일을 gifsicle 로 압축하는 코드는 다음과 같습니다.

역시 chatgpt로 뽑아냈습니다.

 

이렇게 해서 20MB 이내로 잘 뽑아낸 인왕 플레이 움짤은 포스팅에 잘 사용했습니다!!

SNS 공유하기
네이버밴드
카카오톡
페이스북
X(트위터)

최근글
인기글
이모티콘창 닫기
울음
안녕
감사
당황
피폐