Skip to content

Commit

Permalink
add: add initial support for converting from mp4 input,
Browse files Browse the repository at this point in the history
fix: cleanup after failed conversion, improve lang filtering
  • Loading branch information
gacopl committed Jan 5, 2024
1 parent 4d98a7b commit 0aecf63
Showing 1 changed file with 142 additions and 61 deletions.
203 changes: 142 additions & 61 deletions dvmkv2mp4
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ DEBUG="no"
## Check OSTYPE and set ionice level to idle to not hammer disks during conversion
ADDSUBS="no"
## Whether to add subs to mp4 file default no as some bad subs can break the encoding"
SRC_FORMAT="mkv"
## Default input source container used for conversion
DST_SUFFIX="DV-MP4"

if [[ $OSTYPE == 'darwin'* ]]
then
ionc="ionice --low"
Expand All @@ -34,12 +38,14 @@ function print_help {
echo "-r | --remove-source - remove source video after conversion"
echo "-s | --add-subs - add srt subtitles to mp4 as subtitle tracks"
echo "-d | --debug - keep intermediary conversion files"
echo "-m | --mp4-source - enable convert from MP4 source mode instead of MKV"
echo "-f | --override-type - override detected source type to one of dv5,dv7,dv8,hdr10plus
echo "-v | --version - print version"
echo ""
echo "dvmkv2mp4 -l und,pol,eng -r -a # will process any DV/HDR10+ mkvs found in current dir and keep only Undefined, Polish and English tracks, will remove source file once done and will create audio-subs-meta file for future needs"
}

TEMP=$(getopt -o acl:rsdvh --long asm,ext-compat,langs:,remove-source,add-subs,debug,version,help \
TEMP=$(getopt -o acl:rsdmf:vh --long asm,ext-compat,langs:,remove-source,add-subs,debug,mp4-source,override-type:,version,help \
-n 'dvmkv2mp4' -- "$@")

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
Expand All @@ -58,36 +64,69 @@ while true; do
-s | --add-subs ) ADDSUBS="yes"; shift ;;
-c | --ext-compat ) EXT_COMPAT="yes"; shift ;;
-h | --help ) print_help; shift; exit ;;
-m | --mp4-source ) SRC_FORMAT="mp4"; shift ;;
-f | --override-type ) SRC_TYPE="$2"; shift 2 ;;
-l | --langs ) LANGS="$2"; shift 2 ;;
-- ) shift; break ;;
* ) break ;;
esac
done

function cleanup {
while read i;do
rm "`echo "$i" | cut -f1 -d\|`"
done <<< "$(cat audio.exports)"
rm RPU.bin extra.json hdr10plus_metadata.json
rm audio.exports
rm sub.exports
rm tracks.list
rm chapters.list
rm BL*hevc
rm "${input}.dvconverting"
rm -rf ./tmp
if [ $REMOVESOURCE == "yes" ]; then
rm "${input}"
fi
if [ $DEBUG != "yes" ]; then
while read i;do
rm "`echo "$i" | cut -f1 -d\|`"
done <<< "$(cat audio.exports)"

rm RPU.bin extra.json hdr10plus_metadata.json
rm audio.exports
rm sub.exports
rm tracks.list
rm chapters.list
rm BL*hevc
rm "${input}.dvconverting"
rm -rf ./tmp

if [ $exit_code -eq 0 ]; then
if [ $REMOVESOURCE == "yes" ]; then
echo ""
echo "Removing source as requested"
rm "${input}"
if [[ "${input}" == *"${DST_SUFFIX}.mp4" ]]; then
# Move back files to original naming if source already had SUFFIX
echo "Renaming converted files to original source names"
mv "${output}.mp4" "${input}"
mv "${output}.nfo" "${inputbase}.nfo"
for filename in "${inputbase}".mp4.*.*; do
if [ -e "$filename" ]; then
new_filename=$(echo "$filename" | sed 's/\.mp4\.[^.]*\.[^.]*$//')
mv "$filename" "$new_filename"
fi
done
fi
fi
else
echo "Conversion FAILED keeping the source and SRT subtitles, deleting rest"
for i in "$output"*.{srt}; do
cp "$i" "${inputbase}$(echo "$i" | sed -n 's/.*\(\.[^.]*\.\(srt\|sup\)\)$/\1/p')"
echo "${inputbase}$(echo "$i" | sed -n 's/.*\(\.[^.]*\.\(srt\|sup\)\)$/\1/p')"
done
rm "${output}.nfo" "${output}.asm" "${output}.*.srt" "${output}.*.sup" "${input}.dvconverting"
fi
else
rm "${input}.dvconverting"
rm -rf ./tmp
fi
}

function processsubs {
### PROCESS SUBS
inputbase=${input%.mkv}
# COPY EXISTING SRT
for i in "$inputbase".*.srt; do
eval "cp \"$i\" \"`echo "$i" | $sed 's/\ DV\.\|\ HDR10+\.\|HDR\./\./g' | $sed 's/\.\(.*\)\.srt/\ DV-MP4\.\1\.srt/g'`\"";
done
# Only copy SRT if we are changing filename suffix
if [[ "$inputbase" != *"${DST_SUFFIX}.mp4" ]]; then
for i in "$inputbase"*.srt; do
cp "$i" "${output}$(echo "$i" | sed -n 's/.*\(\.[^.]*\.srt\)$/\1/p')"
done
fi
# CONVERT PGS 2 SRT
if [ -s "sub.exports" ] && [ "$(cat sub.exports | grep hdmv_pgs)" != "" ]; then
while read i;do
Expand All @@ -108,21 +147,37 @@ function processsubs {
start=`date +%s`
echo "$HEADER"
echo "Starting Conversions `date`"
for f in *.mkv;do
echo "----------------------------"
for f in *.${SRC_FORMAT};do
echo ""
echo "-------------"
echo "IN: $f"
input="$f"
inputbase=${input%.${SRC_FORMAT}}
ffprobe_source_info=$(ffprobe "$input" 2>&1 | grep DOVI | grep -v comment | sed s/://g)
short_info=$(mediainfo "$input" |awk '/Text #1/{exit}1' | grep -i "^video\|^audio\|format\|commercial\|lang\|title" | cut -f2 -d":" | sed s/,//g | sed s/^Video/\ \|\ Video\ -\ /g | sed s/^Audio/\ \|\ Audio\ -\ /g)
dv=$(mediainfo "$input" | grep 'HDR format.*Dolby Vision')
hdr10plus=$(mediainfo "$input" | grep 'HDR format.*HDR10+')
output=`echo "$input" | $sed "s/\ DV.${SRC_FORMAT}\|\ HDR10+.${SRC_FORMAT}\|\ HDR.${SRC_FORMAT}/.${SRC_FORMAT}/g" | $sed s/\.${SRC_FORMAT}/\ ${DST_SUFFIX}\.${SRC_FORMAT}/g | $sed "s/\ ${DST_SUFFIX} ${DST_SUFFIX}\.${SRC_FORMAT}/\ ${DST_SUFFIX}\.${SRC_FORMAT}/g"`
output="${output%.${SRC_FORMAT}}"
if [[ "${SRC_FORMAT}" == "mp4" ]]; then
if [[ $input == *"${DST_SUFFIX}.mp4.mp4" ]]; then
echo "Input and output would be the same and input already .mp4.mp4 ABORTING!!!"
continue
elif [[ $input == *"${DST_SUFFIX}.mp4" ]]; then
output=${output}.mp4
fi
fi
echo "OUT: $output.mp4"
echo ""

ffstart5_8="$ionc ffmpeg -i \"$input\" -y -loglevel error -stats -map 0:0 -c:v copy -vbsf hevc_mp4toannexb -f hevc BL_RPU.hevc"
ffstart7_1="$ionc ffmpeg -i \"$input\" -y -loglevel error -stats -map 0:0 -c:v copy -vbsf hevc_mp4toannexb -f hevc -"
ffstart7_2="$ionc ffmpeg -i \"$input\" -y -loglevel error -stats -map 0:v:0 -c:v copy -vbsf hevc_mp4toannexb -f hevc BL.hevc -map 0:v:1 -c:v copy -vbsf hevc_mp4toannexb -f hevc -"
ffend7_1="| $ionc dovi_tool -m 2 convert --discard -"
ffend7_2="| $ionc dovi_tool -m 2 extract-rpu - -o RPU.bin"
ffstarthdr10plus="$ionc ffmpeg -i \"$input\" -y -loglevel error -stats -map 0:v:0 -c:v copy -vbsf hevc_mp4toannexb -f hevc BL.hevc -map 0:v:0 -c:v copy -vbsf hevc_mp4toannexb -f hevc -"
ffendhdr10plus="| $ionc hdr10plus_tool extract -o hdr10plus_metadata.json -"
ffprobe_info=$(ffprobe "$input" 2>&1 | grep DOVI | sed s/://g)
short_info=$(mediainfo "$input" |awk '/Text #1/{exit}1' | grep -i "^video\|^audio\|format\|commercial\|lang\|title" | cut -f2 -d":" | sed s/,//g | sed s/^Video/\ \|\ Video\ -\ /g | sed s/^Audio/\ \|\ Audio\ -\ /g)
dv=$(mediainfo "$input" | grep 'HDR format.*Dolby Vision')
hdr10plus=$(mediainfo "$input" | grep 'HDR format.*HDR10+')
output=`echo "$input" | $sed 's/\ DV.mkv\|\ HDR10+.mkv\|\ HDR.mkv/.mkv/g' | $sed s/\.mkv/\ DV-MP4\.mkv/g`
output="${output%.mkv}"

## AUTODETECT SOURCE TYPE AND CONSTRUCT FFMPEG
if [ -z "$hdr10plus" ] && [ -z "$dv" ] || [ -f "${input}.dvconverting" ]; then
Expand All @@ -131,11 +186,24 @@ for f in *.mkv;do
else
touch "${input}.dvconverting"
if [ ! -z "$dv" ]; then
dv_profile=$(mediainfo "$input" | grep "HDR format.*dvhe\." | $sed 's/.*dvhe\.0\(.\).*/\1/')
if { [ "$dv_profile" -ne 4 ] && [ "$dv_profile" -ne 5 ] && [ "$dv_profile" -ne 7 ] && [ "$dv_profile" -ne 8 ]; } ; then
info "Unsupported Dolby Vision profile '$dv_profile'; doing nothing"
rm *.dvconverting
continue
if [[ "$SRC_TYPE" != "" ]]; then
source_type_msg="Source Type overriden to $SRC_TYPE !!!"
echo "$source_type_msg"
created_tag+=" | $source_type_msg"
dv_profile=$SRC_TYPE
else
dv_profile=$(mediainfo "$input" | grep "HDR format.*dvhe\." | $sed 's/.*dvhe\.0\(.\).*/\1/')
if ! [[ $dv_profile =~ ^[0-9]+$ ]]; then
echo "Could not recognize Dolby Vision Profile in source - ABORTING"
echo "Please use -f switch to set profile manually (dv5,dv7,dv8.1,dv8.2,dv8.4)"
rm *.dvconverting
continue
fi
if { [ "$dv_profile" -ne 4 ] && [ "$dv_profile" -ne 5 ] && [ "$dv_profile" -ne 7 ] && [ "$dv_profile" -ne 8 ]; } ; then
info "Unsupported Dolby Vision profile '$dv_profile'; doing nothing"
rm *.dvconverting
continue
fi
fi
vscount=`ffprobe -loglevel error -select_streams v -show_entries stream=type:stream=codec_name:stream=index:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | grep hevc | wc -l`
if [ "$dv_profile" -eq 4 ] || [ "$dv_profile" -eq 5 ] || [ "$dv_profile" -eq 8 ];then
Expand All @@ -147,21 +215,19 @@ for f in *.mkv;do
fi
if [ "$dv_profile" -eq 5 ]; then
dv_target=5
elif [ "$dv_profile" -eq 8 ]; then
dv_target=8
elif [ "$dv_profile" -eq 4 ]; then
dv_target=4
else
dv_target=8
dv_target=8.1
fi
echo "Converting DV$dv_profile to DV$dv_target: \"$input\""
echo "Converting DV$dv_profile $SRC_FORMAT to DV$dv_target mp4"
echo
created_tag+=" | Source - DV$dv_profile $ffprobe_info | $short_info"
created_tag+=" | Source - DV$dv_profile $ffprobe_source_info | $short_info"
elif [ ! -z "$hdr10plus" ]; then
echo "Converting HDR10+ to DV8: \"$input\""
echo "Converting HDR10+ $SRC_FORMAT to DV8"
echo
created_tag+=" | Source - HDR10+ $ffprobe_info | $short_info"
dv_target=8
created_tag+=" | Source - HDR10+ $ffprobe_source_info | $short_info"
dv_target=8.1
ffstring=("$ffstarthdr10plus")
MaxDML=`mediainfo "$input" | grep 'Mastering display luminance' | cut -f 4 -d:`
MaxDML=${MaxDML% cd*}
Expand Down Expand Up @@ -206,13 +272,13 @@ EOF
fi
fi
# SELECT LANG TRACKS
as=$(ffprobe -loglevel error -select_streams a -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | grep $(echo $LANGS | $sed 's/,/,\\|,/g') | $sed 's/,/\|/g')
as=$(ffprobe -loglevel error -select_streams a -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | grep $(echo $LANGS | $sed 's/,/\\|,.*,.*,/g' | $sed 's/^/,.*,.*,/') | $sed 's/,/\|/g' )
if [ "$as" == "" ]; then
ffprobe -loglevel error -select_streams a -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | $sed 's/,/\|/g' > tracks.list
else
echo "$as" > tracks.list
fi
ts=$(ffprobe -loglevel error -select_streams s -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | grep $(echo $LANGS | $sed 's/,/,\\|,/g') | $sed 's/,/\|/g')
ts=$(ffprobe -loglevel error -select_streams s -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | grep $(echo $LANGS | $sed 's/,/\\|,.*,.*,/g' | $sed 's/^/,.*,.*,/') | $sed 's/,/\|/g' )
if [ "$ts" == "" ]; then
ffprobe -loglevel error -select_streams s -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | $sed 's/,/\|/g' >> tracks.list
else
Expand Down Expand Up @@ -264,6 +330,10 @@ EOF
echo "$output.${lang}${id}.ass|$id|ass|$orig_codec|$delay|$lang|$title" >> sub.exports
echo "$output.${lang}${id}.srt|$id|srt|$orig_codec|$delay|$lang|$title" >> sub.exports
fi
if [ "$orig_codec" == "mov_text" ]; then
ffopts="-map 0:$id -c:s:0 srt \"$output.${lang}${id}.srt\""
echo "$output.${lang}${id}.srt|$id|srt|$orig_codec|$delay|$lang|$title" >> sub.exports
fi
if [ "$orig_codec" == "srt" ] || [ "$orig_codec" == "subrip" ]; then
ffopts="-map 0:$id -c:s:0 copy \"$output.${lang}${id}.srt\""
echo "$output.${lang}${id}.srt|$id|srt|$orig_codec|$delay|$lang|$title" >> sub.exports
Expand Down Expand Up @@ -325,24 +395,26 @@ EOF
else
echo "Invalid HDR10+ Metadata ABORTING"
echo "HDR10+ Metadata Frames: $MetadataCount VideoFrames: $FrameCount $MetadataPercent%"
echo "Preserving extracted subs"
echo
inputbase=${input%.mkv}
dvmp4sub=`echo ${input%.mkv} | $sed 's/\ DV\|\ HDR10+//g'`
for i in "$dvmp4sub"\ DV-MP4.*.{srt,sup}; do
eval "cp \"$i\" \"`echo "$i" | $sed 's/\ DV-MP4//g' | $sed \"s/.*\.\(.*\)\.\(s..\)/\$inputbase\.\1\.\2/g\"`\"";
done
rm BL.hevc *DV-MP4.asm *DV-MP4.*.srt *DV-MP4.sup ${input}.dvconverting
exit_code=-1
cleanup
continue
fi
fi


### GRAB CHAPTERS
mkvextract chapters -s "${input}" > chapters.list
if [[ "$SRC_FORMAT" == "mp4" ]]; then
MP4Box -dump-chap-ogg "${input}" && mv "$inputbase".txt chapters.list
else
mkvextract chapters -s "${input}" > chapters.list
fi

echo "compat: $EXT_COMPAT"
echo "$dv_target"

### MUX MP4
if [ $EXT_COMPAT == "yes" ]; then
### FINAL COMPOSITION
if [[ "$EXT_COMPAT" == "yes" && $dv_target -ne 5 ]]; then
echo
echo "Faking DV$dv_target to DV5 for extended device compatiblity"
echo
Expand All @@ -363,7 +435,9 @@ EOF
mp4string+=($mp4opts)
tcount=$((tcount+1))
done <<< "$(cat audio.exports)"

if [ $ADDSUBS == "yes" ]; then
echo "Below subtitles will be added to output file"
while read i;do
stream=`echo "$i" | cut -f1 -d\|`
id=`echo "$i" | cut -f2 -d\|`
Expand All @@ -373,38 +447,45 @@ EOF
lang=`echo "$i" | cut -f6 -d\|`
title=`echo "$i" | cut -f7 -d\|`
echo $stream
echo $codec
if [ "$codec" == "srt" ] && [ -s "$stream" ]; then
mp4opts="-add \"$stream\":lang=$lang:name=\"$title\" -delay $tcount=$delay"
mp4string+=($mp4opts)
tcount=$((tcount+1))
fi
done <<< "$(cat sub.exports)"
fi

if [ -s "chapters.list" ]; then
mp4string+=("-chap chapters.list")
fi

echo ""
echo "Muxing to output file"
echo ""
mp4string+=("-tmp ./tmp -brand mp42isom -ab dby1 -tags comment=\"${created_tag}\" -new \"$output.mp4\"")
echo ${mp4string[*]}
eval ${mp4string[*]}
exit_code=$?

# Preparing NFO
ffprobe_output_info=$(ffprobe "${output}.mp4" 2>&1 | grep DOVI | grep -v comment | sed s/://g)
echo "Created with dvmkv2mp4 $@ ($VERSION)" > "${output}.nfo"
echo "$source_type_msg" >> "${output}.nfo"
echo "--------- SOURCE ------------" >> "${output}.nfo"
echo $ffprobe_info >> "${output}.nfo"
echo $ffprobe_source_info >> "${output}.nfo"
mediainfo "${input}" >> "${output}.nfo"
echo "--------- TARGET ------------" >> "${output}.nfo"
echo $ffprobe_output_info >> "${output}.nfo"
mediainfo "${output}.mp4" >> "${output}.nfo"
if [ $DEBUG != "yes" ]; then
cleanup
else
rm "${input}.dvconverting"
rm -rf ./tmp
fi

cleanup
coment_tag=""
echo "-------------"
done
end=`date +%s`
runtime=$((end-start))
hours=$((runtime / 3600)); minutes=$(( (runtime % 3600) / 60 )); seconds=$(( (runtime % 3600) % 60 ));
echo
echo ""
echo "-------------------------------"
echo "Conversions runtime: $hours:$minutes:$seconds (hh:mm:ss)"
echo

0 comments on commit 0aecf63

Please sign in to comment.