- ProRes Files are about 50% larger
- Cineform is efficient, but no longer supported by OS and Editing software
- Conversion can take a while.
- Making the source files useable again is helpful for revisiting older projects
Rescuing GoPro Cineform Files: Batch Conversion to ProRes
Introduction
If you’ve ever owned an early GoPro camera, you might be familiar with the Cineform codec. Once a widely used format for video compression, it is now facing obsolescence as major platforms like macOS, Windows, Final Cut Pro, and Adobe Premiere Pro have all but dropped support for it. This has left many users with a dilemma: what to do with old footage stored in this now-unusable format?
To solve this issue, I developed a Python tool that can batch convert Cineform files into a more universally supported format: ProRes 422 HQ. This script walks through directories, identifies Cineform files, and converts them while maintaining the original size, aspect ratio, and audio. While it doesn’t correct for the fisheye distortion introduced by GoPro lenses, it ensures that your files are useable in modern editing software without altering the content.
I expanded the script to include all video file types, as I never do a one-shot when I do automation. This allows the script to be useable in a variety of contexts, such as converting from Sony PDW discs or other camera formats that the editing tools or OS utilities don’t support.
In part two I’ll add the ability to generate proxy footage as well
- Cineform is more efficient, space-wise due to limited color space
- ffmpeg is incredibly good when it can use the GPU if available
- Conversion for 60G of source files took about a day on a 96 Core XEON (12x core, with HT, x4 CPUS at 3.1 Ghz )
- Host platform was Linux Ubuntu 22.04
- System has a GPU, but ffmpeg did not use it as the nvenc codec wasn’t installed by ubuntu
Using a GPU for the conversion will be faster, make sure your ffmpeg installation is capable of supporting the GPU, I will do a write up in part 2 of this blog post showing how to configure the system and ffmpeg to use the nvidia encoder.
Understanding the Conversion Process
The script is designed to scan the directory tree of a specified folder, locate supported video files, and convert them to ProRes 422 HQ format using ffmpeg
. The output files retain their original resolution and aspect ratio, and audio is copied without modification to ensure sound quality isn’t affected.
How the Script Works
- Directory Traversal: The script uses Python’s
os.walk
function to recursively scan all subdirectories and locate supported video files with extensions like.mov
,.mp4
,.mkv
, and.avi
. - Metadata Extraction: Before conversion, the script extracts metadata such as:
- Number of frames.
- Playback duration.
- Original video codec.
- Original audio codec.
This is achieved usingffprobe
, a powerful companion tool toffmpeg
. This metadata is displayed before the conversion begins.
- Format Conversion: The script invokes
ffmpeg
to transcode files into ProRes 422 HQ. The command ensures the video is encoded inyuv422p
pixel format and the audio track is copied as-is. - Error Handling: If a file fails during processing, the error is caught, and the script moves on to the next file. This ensures the batch process isn’t interrupted by one problematic file.
- Progress Tracking: Conversion progress is displayed using
tqdm
, a Python library that creates dynamic progress bars. It tracks:- Frames processed.
- Elapsed time.
- Estimated time remaining (ETA).
- Percent completion.
This feature offers users clear feedback on the script’s progress, especially for large files.
- Skipping or Overwriting Existing Files: The script checks if an output file already exists. By default, it skips existing files, but you can override this behavior with the
-f
or--force
flag to overwrite them.
Setting It Up
To run this script, you need Python 3.x installed on your system along with the following libraries:
ffmpeg
andffprobe
: Installable through your system’s package manager (e.g.,brew install ffmpeg
on macOS orapt install ffmpeg
on Ubuntu).tqdm
: Install viapip
:pip install tqdm
Save the script as convert_to_prores.py
, and then run it from the command line. For example:
python convert_to_prores.py /path/to/folder
Why Not Handle Multiple Files Simultaneously?
The script processes one file at a time. This decision was intentional because ffmpeg
already utilizes all available CPU cores during conversion, maximizing efficiency. Running multiple ffmpeg
instances concurrently could lead to excessive resource contention, slowing down the process overall.
Error Handling in Detail
The script anticipates common errors, such as missing directories, unsupported file formats, or issues with ffmpeg
. If an error occurs, it’s logged to the console, and the script continues with the next file. This approach ensures batch processing isn’t derailed by a single problematic file.
The Full Script
Here is the complete Python script:
import os
import sys
import subprocess
import json
import time
from tqdm import tqdm
VIDEO_EXTENSIONS = {".mov", ".mp4", ".mkv", ".avi"}
def get_file_info(file_path):
try:
command = [
"ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
"stream=nb_frames,codec_name,duration", "-of", "json", file_path
]
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
metadata = json.loads(result.stdout)
stream = metadata.get("streams", [])[0]
nb_frames = int(stream.get("nb_frames", 0))
duration = float(stream.get("duration", 0.0))
codec_name = stream.get("codec_name", "Unknown")
command_audio = [
"ffprobe", "-v", "error", "-select_streams", "a:0", "-show_entries",
"stream=codec_name", "-of", "json", file_path
]
result_audio = subprocess.run(command_audio, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
audio_stream = json.loads(result_audio.stdout).get("streams", [{}])[0]
audio_codec = audio_stream.get("codec_name", "Unknown")
return nb_frames, duration, codec_name, audio_codec
except Exception as e:
print(f"Error getting file info for {file_path}: {e}")
return 0, 0.0, "Unknown", "Unknown"
def convert_to_prores(input_file, force_overwrite=False):
nb_frames, duration, video_codec, audio_codec = get_file_info(input_file)
file_dir = os.path.dirname(input_file)
file_name = os.path.basename(input_file)
file_base, file_ext = os.path.splitext(file_name)
if file_ext.lower() not in VIDEO_EXTENSIONS:
print(f"Skipping unsupported file: {input_file}")
return
output_file = os.path.join(file_dir, f"{file_base}_prores{file_ext}")
if os.path.exists(output_file) and not force_overwrite:
print(f"File exists: {output_file}. Skipping (use -f to overwrite).")
return
command = [
"ffmpeg",
"-i", input_file,
"-c:v", "prores_ks",
"-profile:v", "prores_hq",
"-pix_fmt", "yuv422p",
"-c:a", "copy",
output_file
]
print(f"\nProcessing: {file_name}")
print(f"Codec: {video_codec} (Audio: {audio_codec}) ... converting to ProRes 422 HQ")
print(f"Duration: {duration:.2f}s ({nb_frames} frames)\n")
with tqdm(total=nb_frames, desc="Progress", unit="frame") as pbar:
process = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
)
start_time = time.time()
for line in process.stdout:
if "frame=" in line:
try:
frame = int(line.split("frame=")[1].split()[0])
elapsed_time = time.time() - start_time
eta = (elapsed_time / frame) * (nb_frames - frame) if frame > 0 else 0
pbar.n = frame
pbar.set_postfix({
"Elapsed": f"{elapsed_time:.2f}s",
"ETA": f"{eta:.2f}s",
"Percent": f"{(frame / nb_frames) * 100:.2f}%"
})
pbar.update(0)
except (IndexError, ValueError):
continue
process.wait()
pbar.close()
if process.returncode == 0:
print(f"Successfully processed: {output_file}")
else:
print(f"Error processing: {input_file}", file=sys.stderr)
def process_directory(directory, force_overwrite=False):
if not os.path.isdir(directory):
print(f"Error: Directory '{directory}' does not exist.", file=sys.stderr)
return
for root, _, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
convert_to_prores(file_path, force_overwrite)
def main():
if len(sys.argv) < 2:
print("Usage: python convert_to_prores.py [-f] <directory1> [directory2 ...]")
sys.exit(1)
force_overwrite = False
directories = []
for arg in sys.argv[1:]:
if arg in ("-f", "--force"):
force_overwrite = True
else:
directories.append(arg)
if not directories:
print("Error: No directories specified.")
sys.exit(1)
for directory in directories:
print(f"Processing directory: {directory} (Force Overwrite: {force_overwrite})")
process_directory(directory, force_overwrite)
if __name__ == "__main__":
main()
This script provides a robust solution for converting Cineform files, ensuring compatibility with modern video editing workflows.
Discover more from Christine Alifrangis
Subscribe to get the latest posts sent to your email.