38

I have H.265 encoded files that are so resource intensive they don't play well and my conversion software (mencoder) doesn't (currently) support the H.265 format. Can I convert them to H.264 in a command line batch file to rapidly convert the files for re-compression using Devede/OGMrip? Upon searching this site I have not found it discussed so I will post what I feel is a useful question and answer to the question.

0

3 Answers 3

44

Yes, using ffmpeg.

Open a terminal and direct it to the directory containing the H.265 encoded files, assuming you have ffmpeg and the appropriate libraries installed and assuming they are in MKV format copy and paste the following into the terminal window.

INPUT="$1"
for i in *.mkv ; do
    ffmpeg -i "$i" -bsf:v h264_mp4toannexb -sn -map 0:0 -map 0:1 -vcodec libx264 "$i.ts"
    mv "$i.ts" "$i.mpg"
    sleep 3
done

There you have it. This will convert to h.264 in an MPG container in the same directory.

Explanation of the command switches:

  • for i in *.mkv ; do ... done

    This sets up all .mkv files in a directory to be included in the batch process. This may be changed to accommodate the container extension of the files you wish to process.

  • ffmpeg -i "$i" Executes the program ffmpeg and calls for files to be processed.

    • -bsf:v activates the video bit stream filter to be used.

    • h264_mp4toannexb - Is the bit stream filter that is activated.

      Convert an H.264 bitstream from length prefixed mode to start code prefixed mode (as defined in the Annex B of the ITU-T H.264 specification).

      This is required by some streaming formats, typically the MPEG-2 transport stream format (mpegts) processing MKV h.264 (currently)requires this, if is not included you will get an error in the terminal window instructing you to use it.

    • -sn stops the streaming of subtitle streams (for those that do not want subtitles in their video) This is optional and can be removed.

    • -map 0:0 -map 0:1 Tells ffmpeg to only process the first two streams of the file (0:0 is the video stream, 0:1 is the first audio stream of the file). This does two things, strips the excess audio streams, usually the first audio stream is English but not always. Other streams such as embedded subtitles are removed reducing the file size. This is also an optional string. You can use ffprobe to view the streams available in the file. -map is optional and can be discarded from the command.

    • -vcodec libx264 This tells ffmpeg to encode the output to H.264

    • "$i.ts" Saves the output to .ts format, this is useful so as not to overwrite your source files.

  • mv "$i.ts" "$i.mpg" Converts the file extension to MPG in the same directory. This can be set up to send it to any directory you like.

  • sleep 3 - allows the process to rest giving ffmpeg time to queue the next file

6
  • 3
    Excellent! Now we can delete the comments beneath the question. I'd say the sleep 3 is unnecessary (but possible useful for terminating the loop), and INPUT=$1 is meaningless since you are running this directly in an interactive shell.
    – muru
    Dec 8, 2015 at 20:16
  • 13
    Consider stream copying the audio with -c:a copy instead of re-encoding. Also, 0:0 may not always refer to video, and 0:1 may not always refer to audio. You can use a stream specifier to avoid potential issues: -map 0:v -map 0:a if you want all video and audio streams from input 0, or -map 0:v:0 -map 0:a:0 if you only want the first video and audio streams from input 0. If you only want the English audio stream: -map 0:m:language:eng (assuming it is labeled).
    – llogan
    Dec 8, 2015 at 20:44
  • 1
    @LordNeckbeard I'll vote for you to write a complete answer. Sep 24, 2017 at 20:03
  • 1
    @Fran Didn't notice there were so many upvotes on that comment. I added an answer using simpler, less error prone, and more efficient methods.
    – llogan
    Sep 24, 2017 at 23:23
  • 2
    I'd totally go with simply ffmpeg -i $INPUT -vcodec h264 -acodec copy $OUTPUT. Something I use everyday and can actually memorize as opposed to that barrage of meaningless flags which every ffmpeg answer is so infamous for.
    – pronebird
    Apr 7, 2018 at 14:02
25

Batch conversion of H.265 to H.264

These examples were written for recent ffmpeg. Save yourself some trouble and download a recent version. Then put the ffmpeg binary in ~/bin or /usr/local/bin (you may have to log out then log in for it to be noticed).

Matroska output

mkdir h264vids
for f in *.mp4; do ffmpeg -i "$f" -map 0 -c copy -c:v libx264 -crf 23 -preset medium h264vids/"${f%.*}.mkv"; done
  • This example will output to a directory named h264vids.

  • This example assumes your inputs are .mp4. If not, change the .mp4 instance in the example to your input file type, or just use the greedy * by itself.

  • Adjust -crf for quality and -preset for encoding speed/efficiency. Or just remove these options and use the defaults which are fairly good and should suffice for most (the example is using the default values for these options). See FFmpeg Wiki: H.264 for more info on these options.

MP4 output

This one is a little more complicated. This will perform conditional encoding depending if the input audio is AAC or not. If the input audio is AAC then the audio will be stream copied (re-muxed) as is and needless re-encoding is avoided. If the input audio is not AAC then it will be re-encoded to AAC.

Here's a simple script demonstrating how to do this using ffprobe and ffmpeg. Copy and save it to the directory containing your videos to be converted, give it execute permission with chmod +x yourscriptname, then run it with ./yourscriptname.

#!/bin/bash

mkdir h264vids

for f in *.mkv
do
  audioformat=$(ffprobe -loglevel error -select_streams a:0 -show_entries stream=codec_name -of default=nw=1:nk=1 "$f")
  if [ "$audioformat" = "aac" ]; then
    ffmpeg -i "$f" -c:v libx264 -crf 23 -preset medium -c:a copy -movflags +faststart h264vids/"${f%.*}.mp4"
  else
    ffmpeg -i "$f" -c:v libx264 -crf 23 -preset medium -c:a aac -movflags +faststart h264vids/"${f%.*}.mp4"
  fi
done
  • This example will output to a directory named h264vids.

  • This example assumes your inputs are .mkv. If not, change the .mkv instance in the example to your input file type, or just use the greedy * by itself.

  • See note above regarding -crf and -preset.

  • You can pause the encoding with ctrl+z and resume with fg.

2
  • Is it possible to get also the metadata preserved, e.g. GPS location? Aug 12, 2020 at 17:36
  • @VilleLaitila Outputting MKV will preserve it. The MP4 muxer only supports some tags, and it does not support arbitrary tags.
    – llogan
    Aug 13, 2020 at 23:41
2

Considering the previous answers, I came up with the following:

for file in *265.mkv; do nice -n19 ffmpeg -i $file -c copy -c:v libx264 ${file/%265.mkv/264.mkv}; done

This assumes, that the files have always a name ending in 265.mkv. This ending will be replaced with 264.mkv.

If your file naming is different, you need to adapt the command accordingly.

1
  • This is a great answer that I think should be voted higher due to its relevant use of Bash parameter substitution. Dec 4, 2019 at 21:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .