Skip to content

Instantly share code, notes, and snippets.

@ShannonScott
Last active January 15, 2024 03:42
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save ShannonScott/6d807fc59bfa0356eee64fad66f9d9a8 to your computer and use it in GitHub Desktop.
Save ShannonScott/6d807fc59bfa0356eee64fad66f9d9a8 to your computer and use it in GitHub Desktop.
[Transcode h265] Use ffmpeg to transcode a video to h.265 / hvc1 which will play natively on a Mac (and Linux). #tags: video, python, ffmpeg

Transcode Video to h.265

Links

What seems to work

ffmpeg -i <input.???> -c:v libx265 -c:a aac -crf 25 -tag:v hvc1 <output.h265.mp4>

Note the need for the -tag:v hvc1 (see H.265 HEVC support in High Sierra for details). Without the tag, VLC opens the video with out issue, but QuickTime Player does not. The tag fixes the issue with QuickTime.

Probe codecs used

Get audio encoder

ffprobe -v error -select_streams a:0 -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 input.mp4

Get video encoder

ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 input.mp4

Copy audio

ffmpeg -i <infile> -c:v libx265 -crf 28 -c:a copy -tag:v hvc1 -preset slow <outfile>

Transcode audio

ffmpeg -i <infile> -c:v libx265 -crf 28 -c:a aac -b:a 128k -tag:v hvc1 -preset slow <outfile>

Example

Transcode a folder of AVI files:

for filename in *.avi; do
	ffmpeg -i $filename -c:v libx265 -crf 28 -c:a aac -b:a 128k -tag:v hvc1 -preset slow "${filename%.*}.hvc1.mp4"
done

Smart conversion

Avoid converting videos already encoded as HVC1.

for filename in *.{avi,mpeg,mp4,webm}; do
  # Get codecs
  video_codec=$(ffprobe -v error -select_streams 'v:0' -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 "${filename}")
  audio_codec=$(ffprobe -v error -select_streams 'a:0' -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 "${filename}")

  if [ "$video_codec" = "hevc" ]; then
      echo "Skipping (already HVC1): ${filename}"
  else
      #video_opts="-c:v libx265 -crf 28 -tag:v hvc1 -preset slow"
      if [ "$audio_codec" = "aac" ]; then
          #audio_opts="-c:a copy"
          ffmpeg -i "${filename}" -c:v libx265 -crf 28 -tag:v hvc1 -preset slow -c:a copy "${filename%.*}.hvc1.mp4"
      else
          #audio_opts="-c:a aac -b:a 128k"
          ffmpeg -i "${filename}" -c:v libx265 -crf 28 -tag:v hvc1 -preset slow -c:a aac -b:a 128k "${filename%.*}.hvc1.mp4"
      fi
  fi
done

Links

'''
Transcode videos to H265 / hvc1. Skip any file which is already h265 encoded.
https://trac.ffmpeg.org/wiki/Encode/H.265
'''
import os
import sys
import json
from subprocess import call, check_output
def get_codec(filepath, channel='v:0'):
''' Return the codec and codec-tag for a given channel '''
output = check_output(['ffprobe', '-v', 'error', '-select_streams', channel,
'-show_entries', 'stream=codec_name,codec_tag_string', '-of',
'default=nokey=1:noprint_wrappers=1', filepath])
return output.decode('utf-8').split()
for filepath in sys.argv[1:]:
basefilepath, extension = os.path.splitext(filepath)
output_filepath = basefilepath + '.hvc1' + '.mp4'
assert(output_filepath != filepath)
if os.path.isfile(output_filepath):
print('Skipping "{}": file already exists'.format(output_filepath))
continue
print(filepath)
# Get the video channel codec
video_codec = get_codec(filepath, channel='v:0')
if video_codec == []:
print('Skipping: no video codec reported')
continue
# Video transcode options
if video_codec[0] == 'hevc':
if video_codec[1] == 'hvc1':
print('Skipping: already h265 / hvc1')
continue
else:
# Copy stream to hvc1
video_opts = '-c:v copy -tag:v hvc1'
else:
# Transcode to h265 / hvc1
video_opts = '-c:v libx265 -crf 28 -tag:v hvc1 -preset slow -threads 8'
# Get the audio channel codec
audio_codec = get_codec(filepath, channel='a:0')
if audio_codec == []:
audio_opts = ''
elif audio_codec[0] == 'aac':
audio_opts = '-c:a copy'
else:
audio_opts = '-c:a aac -b:a 128k'
call(['ffmpeg', '-i', filepath] + video_opts.split() + audio_opts.split() + [output_filepath])
Copy link

ghost commented Oct 4, 2022

nice. I would just add -tune fastdecode and -movflags "+faststart"
(and .mkv instead of .mp4).

@irem-software
Copy link

.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment