diff options
| author | Pascal Dulieu <pascal@dulieu.uk> | 2026-04-21 21:15:02 +0100 |
|---|---|---|
| committer | Pascal Dulieu <pascal@dulieu.uk> | 2026-04-21 21:23:53 +0100 |
| commit | 7e25e7c9bd135f30b4aaeb608805cd77feef5f1d (patch) | |
| tree | 0789d46b856351dcece6c0e71c8620f3a9a5128c | |
Made-with: Cursor
| -rw-r--r-- | FRIM_x64_version_1.31/FRIMDecode64.exe | bin | 0 -> 592896 bytes | |||
| -rw-r--r-- | README.md | 64 | ||||
| -rw-r--r-- | convert_sbs_to_half_sbs.ps1 | 67 | ||||
| -rw-r--r-- | mkvtoolnix/mkvextract.exe | bin | 0 -> 17226792 bytes | |||
| -rw-r--r-- | mkvtoolnix/mkvinfo.exe | bin | 0 -> 15464488 bytes | |||
| -rw-r--r-- | old/README.md | 3 | ||||
| -rw-r--r-- | old/extract.sh | 5 | ||||
| -rw-r--r-- | old/frimin.bat | 1 | ||||
| -rw-r--r-- | old/frimout.bat | 1 | ||||
| -rw-r--r-- | old/hf-sbs.bat | 1 | ||||
| -rw-r--r-- | process_3d_bluray.ps1 | 237 |
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 Binary files differnew file mode 100644 index 0000000..8cec829 --- /dev/null +++ b/FRIM_x64_version_1.31/FRIMDecode64.exe 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 Binary files differnew file mode 100644 index 0000000..f847ff8 --- /dev/null +++ b/mkvtoolnix/mkvextract.exe diff --git a/mkvtoolnix/mkvinfo.exe b/mkvtoolnix/mkvinfo.exe Binary files differnew file mode 100644 index 0000000..1c4f3c0 --- /dev/null +++ b/mkvtoolnix/mkvinfo.exe 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 |
