aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPascal Dulieu <pascal@dulieu.uk>2026-04-21 21:15:02 +0100
committerPascal Dulieu <pascal@dulieu.uk>2026-04-21 21:23:53 +0100
commit7e25e7c9bd135f30b4aaeb608805cd77feef5f1d (patch)
tree0789d46b856351dcece6c0e71c8620f3a9a5128c
Initial commitHEADmain
Made-with: Cursor
-rw-r--r--FRIM_x64_version_1.31/FRIMDecode64.exebin0 -> 592896 bytes
-rw-r--r--README.md64
-rw-r--r--convert_sbs_to_half_sbs.ps167
-rw-r--r--mkvtoolnix/mkvextract.exebin0 -> 17226792 bytes
-rw-r--r--mkvtoolnix/mkvinfo.exebin0 -> 15464488 bytes
-rw-r--r--old/README.md3
-rw-r--r--old/extract.sh5
-rw-r--r--old/frimin.bat1
-rw-r--r--old/frimout.bat1
-rw-r--r--old/hf-sbs.bat1
-rw-r--r--process_3d_bluray.ps1237
11 files changed, 379 insertions, 0 deletions
diff --git a/FRIM_x64_version_1.31/FRIMDecode64.exe b/FRIM_x64_version_1.31/FRIMDecode64.exe
new file mode 100644
index 0000000..8cec829
--- /dev/null
+++ b/FRIM_x64_version_1.31/FRIMDecode64.exe
Binary files differ
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d549090
--- /dev/null
+++ b/README.md
@@ -0,0 +1,64 @@
+# 3D BluRay Processing Script
+
+PowerShell script that converts 3D BluRay files (MVC format) to Side-by-Side (SBS) and Half-Frame Side-by-Side format for 3D TV and VR headset viewing.
+
+## What it does
+
+1. Extracts the H.264 stream from the input file
+2. Converts MVC to full-frame Side-by-Side (3840x1080) using FRIM decoder
+3. Compresses ProRes full-frame SBS to H.264/AAC for storage efficiency
+4. Creates a half-frame SBS (1920x1080) from ProRes SBS for 3D TV viewing
+
+## File Flow
+
+```text
+Original 3D BluRay File
+ ↓
+H.264 Stream Extraction
+ ↓
+Full-Frame SBS (ProRes, ~200GB)
+ ↓ ↓
+Compressed SBS Half-Frame SBS
+(H.264/AAC) (for 3D TV)
+```
+
+## Requirements
+
+- PowerShell 5.1 or later
+- MKVToolNix (mkvextract and mkvinfo)
+- FRIM decoder (FRIM_x64_version_1.31)
+- FFmpeg
+
+## Installation
+
+1. Download [MKVToolNix](https://mkvtoolnix.download/downloads.html) and extract to the script directory
+2. Download [FRIM decoder](https://www.videohelp.com/software/FRIM) and place in the script directory
+3. Install FFmpeg and add to PATH
+
+## Usage
+
+### Process a single file
+
+```powershell
+.\process_3d_bluray.ps1 "C:\Videos\movie.mkv"
+```
+
+### Process all videos in a folder
+
+```powershell
+.\process_3d_bluray.ps1 "C:\Videos\3D Movies\"
+```
+
+## Output Files
+
+For each input file, the script creates:
+
+- `filename-sbs-compressed.mp4` - Compressed full-frame SBS (for VR headsets)
+- `filename-half-sbs.mp4` - Half-frame SBS for 3D TV
+
+Intermediate files (H.264 stream and ProRes SBS) are automatically cleaned up.
+
+## Notes
+
+- The script only processes files with MVC streams.
+- When using MakeMKV to rip a 3D Blu-ray, make sure to expand each video track and check the unchecked video stream containing 'MVC'.
diff --git a/convert_sbs_to_half_sbs.ps1 b/convert_sbs_to_half_sbs.ps1
new file mode 100644
index 0000000..66fd426
--- /dev/null
+++ b/convert_sbs_to_half_sbs.ps1
@@ -0,0 +1,67 @@
+param(
+ [Parameter(Mandatory=$true)]
+ [string]$InputFile
+)
+
+function New-HalfFrameSBS {
+ param(
+ [string]$SbsFile,
+ [string]$OutputFile
+ )
+
+ Write-Host "Checking input file dimensions..." -ForegroundColor Yellow
+ try {
+ $probeOutput = & ffprobe -v quiet -print_format json -show_streams "`"$SbsFile`""
+ $probeJson = $probeOutput | ConvertFrom-Json
+ $videoStream = $probeJson.streams | Where-Object { $_.codec_type -eq "video" } | Select-Object -First 1
+ if ($videoStream) {
+ $width = $videoStream.width
+ $height = $videoStream.height
+ Write-Host "Input dimensions: ${width}x${height}" -ForegroundColor Cyan
+ } else {
+ Write-Host "Could not find video stream information" -ForegroundColor Red
+ }
+ } catch {
+ Write-Host "Error getting input dimensions: $($_.Exception.Message)" -ForegroundColor Red
+ $inputInfo = & ffmpeg -i "`"$SbsFile`"" 2>&1 | Select-String "Stream.*Video.*(\d+)x(\d+)"
+ if ($inputInfo -and $inputInfo.Matches.Groups.Count -ge 3) {
+ $width = $inputInfo.Matches.Groups[1].Value
+ $height = $inputInfo.Matches.Groups[2].Value
+ Write-Host "Input dimensions (fallback): ${width}x${height}" -ForegroundColor Cyan
+ }
+ }
+
+ $halfFrameArgs = @("-i", "`"$SbsFile`"", "-vsync", "vfr", "-vf", "`"scale=iw/2:ih,setdar=16/9`"", "-c:v", "libx264", "-profile:v", "main", "-level", "4.1", "-pix_fmt", "yuv420p", "-crf", "23", "-refs", "3", "-bf", "2", "-g", "30", "-keyint_min", "23", "-sc_threshold", "40", "-c:a", "aac", "-b:a", "192k", "-ar", "48000", "-ac", "2", "`"$OutputFile`"")
+ Write-Host "Running: ffmpeg $($halfFrameArgs -join ' ')" -ForegroundColor Gray
+ & ffmpeg $halfFrameArgs
+
+ if (Test-Path $OutputFile) {
+ Write-Host "Checking output file dimensions..." -ForegroundColor Yellow
+ try {
+ $probeOutput = & ffprobe -v quiet -print_format json -show_streams "`"$OutputFile`""
+ $probeJson = $probeOutput | ConvertFrom-Json
+ $videoStream = $probeJson.streams | Where-Object { $_.codec_type -eq "video" } | Select-Object -First 1
+ if ($videoStream) {
+ $outWidth = $videoStream.width
+ $outHeight = $videoStream.height
+ Write-Host "Output dimensions: ${outWidth}x${outHeight}" -ForegroundColor Cyan
+ }
+ } catch {
+ Write-Host "Error getting output dimensions: $($_.Exception.Message)" -ForegroundColor Red
+ $outputInfo = & ffmpeg -i "`"$OutputFile`"" 2>&1 | Select-String "Stream.*Video.*(\d+)x(\d+)"
+ if ($outputInfo -and $outputInfo.Matches.Groups.Count -ge 3) {
+ $outWidth = $outputInfo.Matches.Groups[1].Value
+ $outHeight = $outputInfo.Matches.Groups[2].Value
+ Write-Host "Output dimensions (fallback): ${outWidth}x${outHeight}" -ForegroundColor Cyan
+ }
+ }
+ }
+
+ if (-not (Test-Path $OutputFile)) {
+ throw "Failed to create half-frame SBS video"
+ }
+ Write-Host "Half-frame SBS creation completed" -ForegroundColor Green
+}
+$inputFileItem = Get-Item $InputFile
+$OutputFile = Join-Path $inputFileItem.DirectoryName ($inputFileItem.BaseName + "-half-sbs.mp4")
+New-HalfFrameSBS -SbsFile $InputFile -OutputFile $OutputFile \ No newline at end of file
diff --git a/mkvtoolnix/mkvextract.exe b/mkvtoolnix/mkvextract.exe
new file mode 100644
index 0000000..f847ff8
--- /dev/null
+++ b/mkvtoolnix/mkvextract.exe
Binary files differ
diff --git a/mkvtoolnix/mkvinfo.exe b/mkvtoolnix/mkvinfo.exe
new file mode 100644
index 0000000..1c4f3c0
--- /dev/null
+++ b/mkvtoolnix/mkvinfo.exe
Binary files differ
diff --git a/old/README.md b/old/README.md
new file mode 100644
index 0000000..e942eab
--- /dev/null
+++ b/old/README.md
@@ -0,0 +1,3 @@
+# Old original manual scripts
+
+Thanks to [Scott Garrett](https://www.technomancer.com/archives/685) for the original scripts
diff --git a/old/extract.sh b/old/extract.sh
new file mode 100644
index 0000000..8f51aaa
--- /dev/null
+++ b/old/extract.sh
@@ -0,0 +1,5 @@
+#!/bin/bash -x
+
+FILE="$1"
+
+mkvextract -f tracks "${FILE}" 0:"$( basename "${FILE}" .mkv )".h264 --fullraw \ No newline at end of file
diff --git a/old/frimin.bat b/old/frimin.bat
new file mode 100644
index 0000000..e00f4ba
--- /dev/null
+++ b/old/frimin.bat
@@ -0,0 +1 @@
+ffmpeg -y -f rawvideo -vcodec rawvideo -s 3840x1080 -pix_fmt yuv420p -thread_queue_size 128 -r 24000/1001 -i \\.\pipe\frimdata -i %1 -c:v prores_ks -profile:v 3 -vendor apl0 -pix_fmt yuv422p10le -c:a copy -sn -map 0:v:0 -map 1 -map -1:v %1-sbs.mkv \ No newline at end of file
diff --git a/old/frimout.bat b/old/frimout.bat
new file mode 100644
index 0000000..7e78f8c
--- /dev/null
+++ b/old/frimout.bat
@@ -0,0 +1 @@
+FRIM_x64_version_1.31\x64\FRIMDecode64.exe -i:mvc %1 -sbs -o \\.\pipe\frimdata \ No newline at end of file
diff --git a/old/hf-sbs.bat b/old/hf-sbs.bat
new file mode 100644
index 0000000..fbdb027
--- /dev/null
+++ b/old/hf-sbs.bat
@@ -0,0 +1 @@
+ffmpeg -i %1 -vsync vfr -vf "scale=iw/2:ih" -c:v libx264 -crf 23 -c:a aac -b:a 192k output_half_sbs.mp4 \ No newline at end of file
diff --git a/process_3d_bluray.ps1 b/process_3d_bluray.ps1
new file mode 100644
index 0000000..2ec8ccb
--- /dev/null
+++ b/process_3d_bluray.ps1
@@ -0,0 +1,237 @@
+param(
+ [Parameter(Mandatory = $true)]
+ [string]$InputPath
+)
+function Get-Mkvinfo {
+ $possiblePaths = @(
+ "$env:USERPROFILE\Downloads\mkvtoolnix\mkvinfo.exe",
+ "$env:USERPROFILE\Downloads\mkvtoolnix\mkvinfo",
+ "mkvinfo.exe",
+ "mkvinfo",
+ "./mkvtoolnix/mkvinfo.exe",
+ "./mkvtoolnix/mkvinfo"
+ )
+ foreach ($path in $possiblePaths) {
+ if (Get-Command $path -ErrorAction SilentlyContinue) {
+ return $path
+ }
+ }
+ return $null
+}
+function Test-MVCContent {
+ param(
+ [string]$InputFile,
+ [string]$MkvinfoPath
+ )
+ try {
+ $trackInfo = & $MkvinfoPath $InputFile 2>&1
+ if ($trackInfo -match "Block addition ID type: 1836475203 \(mvcC\)") {
+ return $true
+ }
+ if ($trackInfo -match "Video stereo mode: \d+") {
+ return $true
+ }
+ if ($trackInfo -match "Block addition ID extra data.*mvcC|mvcC.*Block addition") {
+ return $true
+ }
+ if ($trackInfo -match "Codec ID: V_MPEG4/ISO/AVC" -and $trackInfo -match "Block addition") {
+ return $true
+ }
+ return $false
+ }
+ catch {
+ Write-Warning "Could not analyze file for MVC streams: $($_.Exception.Message)"
+ return $false
+ }
+}
+function Get-Mkvextract {
+ $possiblePaths = @(
+ "$env:USERPROFILE\Downloads\mkvtoolnix\mkvextract.exe",
+ "$env:USERPROFILE\Downloads\mkvtoolnix\mkvextract",
+ "mkvextract.exe",
+ "mkvextract",
+ "./mkvtoolnix/mkvextract.exe",
+ "./mkvtoolnix/mkvextract"
+ )
+ foreach ($path in $possiblePaths) {
+ if (Get-Command $path -ErrorAction SilentlyContinue) {
+ return $path
+ }
+ }
+ return $null
+}
+function Invoke-H264Extraction {
+ param(
+ [string]$InputFile,
+ [string]$MkvextractPath,
+ [string]$H264File
+ )
+ Write-Host "`nStep 1: Extracting H.264 stream..." -ForegroundColor Cyan
+ $extractArgs = @("-f", "tracks", "`"$InputFile`"", "0:`"$H264File`"", "--fullraw")
+ Write-Host "Running: $MkvextractPath $($extractArgs -join ' ')" -ForegroundColor Gray
+ & $MkvextractPath $extractArgs
+ if (-not (Test-Path $H264File)) {
+ throw "Failed to extract H.264 stream"
+ }
+ Write-Host "H.264 extraction completed" -ForegroundColor Green
+}
+function New-FullFrameSBS {
+ param(
+ [string]$H264File,
+ [string]$InputFile,
+ [string]$SbsFile
+ )
+ Write-Host "`nStep 2: Decoding MVC to SBS..." -ForegroundColor Cyan
+ $pipeName = "\\.\pipe\frimdata"
+ $frimProcess = Start-Process -FilePath "FRIM_x64_version_1.31\FRIMDecode64.exe" -ArgumentList "-i:mvc", "`"$H264File`"", "-sbs", "-o", $pipeName -PassThru -NoNewWindow
+ Start-Sleep -Seconds 2
+ Write-Host "Running ffmpeg to create full-frame SBS..." -ForegroundColor Gray
+ $ffmpegArgs = @("-y", "-f", "rawvideo", "-vcodec", "rawvideo", "-s", "3840x1080", "-pix_fmt", "yuv420p", "-thread_queue_size", "128", "-r", "24000/1001", "-i", "`"$pipeName`"", "-i", "`"$InputFile`"", "-c:v", "prores_ks", "-profile:v", "3", "-vendor", "apl0", "-pix_fmt", "yuv422p10le", "-c:a", "copy", "-sn", "-map", "0:v:0", "-map", "1", "-map", "-1:v", "`"$SbsFile`"")
+ Write-Host "Running: ffmpeg $($ffmpegArgs -join ' ')" -ForegroundColor Gray
+ & ffmpeg $ffmpegArgs
+ $frimProcess.WaitForExit()
+ if (-not (Test-Path $SbsFile)) {
+ throw "Failed to create full-frame SBS video"
+ }
+ Write-Host "Full-frame SBS creation completed" -ForegroundColor Green
+}
+function New-HalfFrameSBS {
+ param(
+ [string]$SbsFile,
+ [string]$OutputFile
+ )
+ Write-Host "`nStep 3: Creating half-frame SBS for 3D TV..." -ForegroundColor Cyan
+ $halfFrameArgs = @("-i", "`"$SbsFile`"", "-vsync", "vfr", "-vf", "`"scale=iw/2:ih`"", "-c:v", "libx264", "-profile:v", "main", "-level", "4.1", "-pix_fmt", "yuv420p", "-crf", "23", "-refs", "3", "-bf", "2", "-g", "30", "-keyint_min", "23", "-sc_threshold", "40", "-c:a", "aac", "-b:a", "192k", "-ar", "48000", "-ac", "2", "`"$OutputFile`"")
+ Write-Host "Running: ffmpeg $($halfFrameArgs -join ' ')" -ForegroundColor Gray
+ & ffmpeg $halfFrameArgs
+ if (-not (Test-Path $OutputFile)) {
+ throw "Failed to create half-frame SBS video"
+ }
+ Write-Host "Half-frame SBS creation completed" -ForegroundColor Green
+}
+function New-CompressedSBS {
+ param(
+ [string]$SbsFile,
+ [string]$CompressedSbsFile
+ )
+ Write-Host "`nStep 4: Compressing full-frame SBS to H.264/AAC..." -ForegroundColor Cyan
+ $compressArgs = @("-i", "`"$SbsFile`"", "-c:v", "libx264", "-preset", "slow", "-crf", "18", "-c:a", "aac", "-b:a", "320k", "-movflags", "+faststart", "`"$CompressedSbsFile`"")
+ Write-Host "Running: ffmpeg $($compressArgs -join ' ')" -ForegroundColor Gray
+ & ffmpeg $compressArgs
+ if (-not (Test-Path $CompressedSbsFile)) {
+ throw "Failed to create compressed SBS video"
+ }
+ Write-Host "Compressed SBS creation completed" -ForegroundColor Green
+}
+function Invoke-SingleFileProcessing {
+ param(
+ [string]$InputFile,
+ [string]$MkvextractPath
+ )
+ $fileInfo = Get-Item $InputFile
+ $baseName = [System.IO.Path]::GetFileNameWithoutExtension($fileInfo.Name)
+ $directory = $fileInfo.DirectoryName
+ $h264File = Join-Path $directory "$baseName.h264"
+ $sbsFile = Join-Path $directory "$baseName-sbs.mkv"
+ $compressedSbsFile = Join-Path $directory "$baseName-sbs-compressed.mp4"
+ $outputFile = Join-Path $directory "$baseName-half-sbs.mp4"
+ Write-Host "`nProcessing 3D BluRay: $InputFile" -ForegroundColor Green
+ Write-Host "Output directory: $directory" -ForegroundColor Yellow
+ Invoke-H264Extraction -InputFile $InputFile -MkvextractPath $mkvextractPath -H264File $h264File
+ New-FullFrameSBS -H264File $h264File -InputFile $InputFile -SbsFile $sbsFile
+ New-CompressedSBS -SbsFile $sbsFile -CompressedSbsFile $compressedSbsFile
+ New-HalfFrameSBS -SbsFile $sbsFile -OutputFile $outputFile
+ Write-Host "Processing completed successfully!" -ForegroundColor Green
+ Write-Host "Output file: $outputFile" -ForegroundColor Yellow
+ $fileSize = [math]::Round((Get-Item $outputFile).Length / 1MB, 2)
+ Write-Host "File size: $fileSize MB" -ForegroundColor Yellow
+ Write-Host "Cleaning up intermediate files..." -ForegroundColor Cyan
+ if (Test-Path $h264File) {
+ Remove-Item $h264File -Force
+ Write-Host "Removed: $h264File" -ForegroundColor Gray
+ }
+ if (Test-Path $sbsFile) {
+ Remove-Item $sbsFile -Force
+ Write-Host "Removed: $sbsFile" -ForegroundColor Gray
+ }
+ Write-Host "Cleanup completed!" -ForegroundColor Green
+ Write-Host "Compressed SBS file kept: $compressedSbsFile" -ForegroundColor Yellow
+}
+try {
+ if (-not (Test-Path $InputPath)) {
+ Write-Error "Input path '$InputPath' not found!"
+ exit 1
+ }
+ $mkvextractPath = Get-Mkvextract
+ if (-not $mkvextractPath) {
+ Write-Error "mkvextract not found! Please ensure MKVToolNix is installed in your Downloads folder or in PATH."
+ exit 1
+ }
+ $mkvinfoPath = Get-Mkvinfo
+ if (-not $mkvinfoPath) {
+ Write-Error "mkvinfo not found! Please ensure MKVToolNix is installed in your Downloads folder or in PATH."
+ exit 1
+ }
+ Write-Host "Using mkvextract: $mkvextractPath" -ForegroundColor Gray
+ Write-Host "Using mkvinfo: $mkvinfoPath" -ForegroundColor Gray
+ $inputItem = Get-Item $InputPath
+ if ($inputItem.PSIsContainer) {
+ Write-Host "Processing directory: $InputPath" -ForegroundColor Green
+ $videoExtensions = @("*.mkv", "*.mp4", "*.avi", "*.m4v", "*.mov", "*.wmv", "*.flv", "*.webm")
+ $videoFiles = @()
+ foreach ($ext in $videoExtensions) {
+ $videoFiles += Get-ChildItem -Path $InputPath -Filter $ext -File
+ }
+ if ($videoFiles.Count -eq 0) {
+ Write-Host "No video files found in directory: $InputPath" -ForegroundColor Yellow
+ exit 0
+ }
+ Write-Host "Found $($videoFiles.Count) video files" -ForegroundColor Yellow
+ $processedCount = 0
+ $skippedCount = 0
+ $noMvcCount = 0
+ foreach ($videoFile in $videoFiles) {
+ $baseName = [System.IO.Path]::GetFileNameWithoutExtension($videoFile.Name)
+ $outputFile = Join-Path $videoFile.DirectoryName "$baseName-half-sbs.mp4"
+ if (Test-Path $outputFile) {
+ Write-Host "`nSkipping $($videoFile.Name) - output already exists: $outputFile" -ForegroundColor Yellow
+ $skippedCount++
+ continue
+ }
+ Write-Host "`nAnalyzing $($videoFile.Name) for MVC streams..." -ForegroundColor Cyan
+ if (-not (Test-MVCContent -InputFile $videoFile.FullName -MkvinfoPath $mkvinfoPath)) {
+ Write-Host "Skipping $($videoFile.Name) - no MVC streams detected (not a 3D BluRay)" -ForegroundColor Yellow
+ $noMvcCount++
+ continue
+ }
+ Write-Host "MVC streams detected in $($videoFile.Name) - proceeding with processing" -ForegroundColor Green
+ try {
+ Invoke-SingleFileProcessing -InputFile $videoFile.FullName -MkvextractPath $mkvextractPath
+ $processedCount++
+ }
+ catch {
+ Write-Error "Failed to process $($videoFile.Name): $($_.Exception.Message)"
+ continue
+ }
+ }
+ Write-Host "`nBatch processing completed!" -ForegroundColor Green
+ Write-Host "Processed: $processedCount files" -ForegroundColor Yellow
+ Write-Host "Skipped: $skippedCount files (already exist)" -ForegroundColor Yellow
+ Write-Host "No MVC: $noMvcCount files (not 3D BluRay)" -ForegroundColor Yellow
+ }
+ else {
+ Write-Host "Processing single file: $InputPath" -ForegroundColor Green
+ Write-Host "Analyzing file for MVC streams..." -ForegroundColor Cyan
+ if (-not (Test-MVCContent -InputFile $InputPath -MkvinfoPath $mkvinfoPath)) {
+ Write-Error "File does not contain MVC streams - this is not a 3D BluRay file!"
+ exit 1
+ }
+ Write-Host "MVC streams detected - proceeding with processing" -ForegroundColor Green
+ Invoke-SingleFileProcessing -InputFile $InputPath -MkvextractPath $mkvextractPath
+ }
+}
+catch {
+ $errorMessage = "Error during processing: " + $_.Exception.Message
+ Write-Error $errorMessage
+ exit 1
+} \ No newline at end of file