Created
January 19, 2022 18:19
-
-
Save A2va/305e050bfb4d9e734bcf793391f96213 to your computer and use it in GitHub Desktop.
Convert media file to a chromecast compatible
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# PowerShell version 7 and higher, the default intalled on Windows is 5.1 | |
# https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.1 | |
# on Windows Terminal a profiles for PowerShell 7 is directly created. | |
# ffmpeg and ffprobe must be on PATH. | |
<# | |
.DESCRIPTION | |
Convert a media file to format compatible with chromecast. | |
PowerShell version 7 and higher is needed, the default intalled on Windows is 5.1. | |
.PARAMETER i | |
Input file to be converted. | |
.PARAMETER remove-karaoke | |
Remove karaoke effect from subtitle | |
.PARAMETER burn-sub | |
Burn subtitle on video stream. | |
.PARAMETER force-search-replace | |
Force srt subtitle. If a subtitle is already compatible with Chromecast, convert it to srt. | |
.PARAMETER search | |
Search a text in subtitle. | |
.PARAMETER replace | |
Replace search text by replace text. | |
.EXAMPLE | |
chromecast.ps1 -i test.mp4 -remove-karaoke | |
.LINK | |
Install powershell 7: https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.1 | |
#> | |
param ( | |
[Alias('input')] | |
# Validate file path | |
[ValidateScript({ | |
if (-Not ($_ | Test-Path) ) { | |
throw "File or folder does not exist" | |
} | |
if (-Not ($_ | Test-Path -PathType Leaf) ) { | |
throw "The Path argument must be a file. Folder paths are not allowed." | |
} | |
return $true | |
})] [string] $i, | |
[switch]${remove-karaoke} = $false, | |
[switch]${burn-sub} = $false, | |
[string[]] $search = @(), | |
[string[]] $replace = @() | |
) | |
function Write-ColorOutput($ForegroundColor) { | |
# Save the current color | |
$fc = $host.UI.RawUI.ForegroundColor | |
# Set the new color | |
$host.UI.RawUI.ForegroundColor = $ForegroundColor | |
# Output | |
if ($args) { | |
Write-Output $args | |
} | |
else { | |
$input | Write-Output | |
} | |
# restore the original color | |
$host.UI.RawUI.ForegroundColor = $fc | |
} | |
##################################### | |
# Check ffmpeg and ffprobe available | |
##################################### | |
ffprobe.exe | |
if ($LastExitCode -ne 0) { | |
Write-ColorOutput red ("ffprobe error.Maybe the executable is not on path") | |
} | |
ffmpeg.exe | |
if ($LastExitCode -ne 0) { | |
Write-ColorOutput red ("ffprobe error.Maybe the executable is not on path") | |
} | |
####################### | |
# Check nvenc available | |
####################### | |
$hwaccel_available = $false | |
# $list = "encoders","decoders","filters" | |
$list = "encoders" | |
foreach ($i in $list) { | |
$output = ffmpeg -hide_banner -$i | |
if ($output -match "nvenc") { | |
$hwaccel_available = $true | |
} | |
} | |
$filename = Convert-Path $i | |
if (Test-Path -Path $filename) { | |
Write-ColorOutput red ("No file provided") | |
Exit | |
} | |
if($search.length -ne $replace.length) { | |
Write-ColorOutput red ("No search/replace arguments have not the same length") | |
Exit | |
} | |
# Final ffmpeg cmd | |
$ffmpeg_input = "" | |
$pre_input = "" # Pre input cmd | |
$post_input = "" # Post input cmd | |
$remux = $false | |
# Move flag value to better variable name | |
$burn_sub = ${burn-sub} | |
$remove_karaoke = ${remove-karaoke} | |
# The reaseon of 2>&1 in ffprobe cmd it's because ffprobe and ffmpeg | |
# output on stderr and not stdout. The 2>&1 operator redirect stderr to stdout. | |
####################### | |
# Check subtitle codec | |
####################### | |
# Select first subtitle stream | |
$subtitle_probe = ffprobe.exe -v error -show_format -show_streams -select_streams s:0 $filename 2>&1 | |
# Test ffprobe error | |
if ($LastExitCode -ne 0) { | |
Write-Output "ffprobe error: $audio_probe. Error code: $LastExitCode" | |
Exit | |
} | |
# Codec name | |
$subtile_name = ($subtitle_probe[2] -split "=")[1] | |
$supported_subtitle = "ttml", "webvtt", "srt", "wav", "vorbis" | |
# TODO Check more sub codec | |
if ($subtile_name -ne "srt") { | |
$remux = $true | |
# The subtitle codec might be not work for chromecast | |
Write-Output $subtitle_probe[2] | |
# Get subtitle stream index | |
$index = ($subtitle_probe[1] -split "=")[1] | |
# Extract subtitle | |
ffmpeg -y -i $filename -map 0:$index original.$subtile_name | |
# If ass, ssa format and burn flag write to video stream | |
if ($burn_sub) { | |
$ffmpeg_input = "-i original.$subtile_name" # Set subtitle input | |
$post_input = "-vf subtitles=original.$subtile_name" # Flag to burn on video | |
} | |
else { | |
# Remove karaoke | |
if ($remove_karaoke) { | |
# TODO Search/Replace | |
# Improve subs rewriting | |
$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8' | |
$replace = @() | |
$remove_style = "karaoke" | |
foreach ($line in Get-Content .\original.$subtile_name) { | |
if (-Not ($line | Select-String -pattern $remove_style)) { | |
$replace += $line | |
} | |
} | |
$replace | Out-File -Append ./out.ass -Encoding utf8 | |
# Convert to new format | |
ffmpeg -y -i out.ass -scodec srt cache.srt | |
} | |
else { | |
# Convert to new format | |
ffmpeg -y -i original.$subtile_name -scodec srt cache.srt | |
} | |
$ffmpeg_input = "-i cache.srt" | |
$post_input = "-scodec srt" | |
} | |
} | |
else { | |
# Placeholder for encode audio or video (maybe not needed) | |
$post_input = "-scodec copy" | |
} | |
####################### | |
# Check audio codec | |
####################### | |
# Select first audio stream | |
$audio_probe = ffprobe.exe -v error -show_format -show_streams -select_streams a:0 $filename 2>&1 | |
# Test ffprobe error | |
if ($LastExitCode -ne 0) { | |
Write-Output "ffprobe error: $audio_probe. Error code: $LastExitCode" | |
Exit | |
} | |
# Codec name | |
$codec_name = ($audio_probe[2] -split "=")[1] | |
# Chromecast supported codec | |
# https://support.google.com/chromecast/answer/6279377?hl=en | |
$supported_codec = "flac", "aac", "mp3", "wav", "vorbis" | |
# Wrong codec for chromecast | |
if (-Not $supported_codec.Contains($codec_name)) { | |
$remux = $true | |
$post_input = "$post_input -acodec mp3" # Set to mp3 by default | |
} | |
else { | |
# In case of flac | |
if ($codec_name -eq "flac") { | |
$sample_rate = [int]($audio_probe[9] -split "=")[1] | |
$bits_per_raw_sample = [int]($audio_probe[23] -split "=")[1] | |
# Set max sample rate | |
if (($sample_rate -ne -1) -and ($sample_rate -gt 96000)) { | |
$post_input = "$post_input -sample_rate 96000" | |
} | |
# Set bits per raw sample | |
if ($bits_per_raw_sample -gt 24) { | |
$post_input = "$post_input -bits_per_raw_sample 24" | |
} | |
} | |
} | |
####################### | |
# Check video codec | |
####################### | |
$video_probe = ffprobe.exe -v error -show_format -show_streams -select_streams v:0 $filename 2>&1 | |
# Test ffprobe error | |
if ($LastExitCode -ne 0) { | |
Write-Output "ffprobe error: $video_probe. Error code: $LastExitCode" | |
Exit | |
} | |
# Codec name | |
$codec_name = ($video_probe[2] -split "=")[1] | |
# Chrome cast support only h264 or vp8 | |
# vp8 is ignored in this case | |
# https://developers.google.com/cast/docs/media | |
if ($codec_name -ne "h264") { | |
$remux = $true | |
$post_input = $post_input + $(if ($hwaccel_available) { " -vcodec h264_nvenc" } else { " -vcodec h264" }) | |
$post_input = "$post_input -pix_fmt yuv420p" | |
} | |
else { | |
# TODO Check if it's needed | |
$bad_fmt = ($video_probe[17] -split "=")[1] -ne "yuv420p" | |
$codec = if ($hwaccel_available) { " -vcodec h264_nvenc" } else { " -vcodec h264" } | |
# Encode must needed for burn sub or bad fmt | |
$post_input = $post_input + $(if ($burn_sub) { $codec } else { " -vcodec copy" }) | |
# Check pix fmt | |
if ($bad_fmt) { | |
$post_input = "$post_input -pix_fmt yuv420p" | |
} | |
} | |
####################### | |
# Final cmd | |
####################### | |
if ($remux) { | |
$file = [System.IO.Path]::GetFileNameWithoutExtension($filename) | |
$ext = [System.IO.Path]::GetExtension($filename) | |
# Add flag for harware acceleration | |
if ($hwaccel_available) { | |
$pre_input = "-init_hw_device cuda=cuda:0 -hwaccel cuda -filter_hw_device cuda" | |
} | |
$ffmpeg_input = "$ffmpeg_input -i $filename" # Add video file to input | |
$directory = [System.IO.Path]::GetDirectoryName($filename) | |
$output_file = "$directory/$($file)_converted$($ext)" # Concat filename converted and extension | |
$cmd_str = "ffmpeg $pre_input $ffmpeg_input $post_input $output_file" | |
Invoke-Expression $cmd_str | |
if ($LastExitCode -ne 0) { | |
Write-Output "ffmpeg error: $out. Error code: $LastExitCode" | |
Write-Output "Maybe ffmpeg nvenc is enable but you doesn't have a nvidia gpu" | |
Exit | |
} | |
} | |
Remove-Item -Path ./out.ass | |
Remove-Item -Path ./cache.srt | |
Remove-Item -Path "original.$subtile_name" | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment