Friday, May 3, 2013

Convert MKV files to MP4 files

Note the solution described here uses Linux with avconv or FFmpeg  to do the file conversion, however the converted MP4 file can be played anywhere. Since FFmpeg is also available for Windows, my Linux based solution can also be implemented within Windows. Bottom line - don't let my use of Linux spook you!


Short version

avconv -i input.mkv -vcodec copy -acodec copy output.mp4
or
ffmpeg -i input.mkv -vcodec copy -acodec copy output.mp4

which can be run on multiple files via a simple Bash Shell Script:

#!/bin/bash
#avconv --help
IFS=$'\n' 
clear
for filename in *.mkv
do
echo "$filename"
newfilename="${filename%.mkv}.mp4"
echo "${newfilename}"
avconv -i $filename -vcodec copy -acodec copy $newfilename
done


Long Version
By default many media devices will only play MP4 (MPEG-4 Part 14) files, particularly Apple devices such as iPads and iPods. Google's Android OS is much more forgiving about what media files it will play.

Ultimately it is usually possible to install another media player app onto portable media devices (e.g. Apple's iPad / iPod for example) and get around non-mp4 container format restrictions. Sometimes this approach can be inconvenient or impossible in the case of devices when there is no possibility of installing alternate media players.

The question then becomes 'How to convert your existing files (in MKV container format or an other format for that matter) into acceptable MP4 container files?'

Note that it is important to realise that MKV and MP4 are just container file formats. In the words of Wikipedia 'A container or wrapper format is a metafile format whose specification describes how different data elements and metadata coexist in a computer file.' 

Container file formats are used to store video, audio and subtitles etc. So you can have an MKV file for example that has completely different video codec, audio codec and subtitle format from an otherwise apparently identical MKV file (e.g. the video may be in H.264/AVC or mpeg2, DIVX, XVID etc or mp3 rather than AAC or FLAC in the case of audio). So the MKV and MP4 container file formats just contain a mixture of video and audio codecs with possibly some subtitles. This is why a video file with an MP4, MKV or AVI extension may play but another file with the same extension will not play.

Understanding that MP4 and MKV are just container files, essentially envelopes containing various video and audio codecs etc, is really important. Once you understand this distinction everything else follows.

It is the codecs used within the container file itself that are important, but in this case (where the MP4 container file format is supported but not MKV or AVI container file formats - for example) the problem is that only the MP4 container file is supported.

I normally create my video file collection (from conversions of DVDs and Blu-rays etc) using HandBrake. I use the High Profile preset (for a high quality conversion) and the lower quality iPod Legacy preset to view on my Google Nexus tablets (7") and Samsung Galaxy S3 mini mobiles.

Both of my chosen HandBrake presets use the H.264/AVC (MPEG-4 Part 10) video codec which is widely supported by most modern devices. Cheaper devices may use the MPEG-4 Part 2 (e.g. DIVX / XVID) or even MPEG-2 Part 2 (e.g. DVD) video format.

My default container file format is MKV since it supports virtually any video and audio codecs (see Wikipedia's discussion), unlike the more limited MP4 container file format. To convert from MKV to MP4 the simplest approach is to use FFmpeg or the fork avconv (Libav). Note that Ubuntu uses avconv (Libav), hence I do the same.

Since I already have my default MKV file in the video and audio codecs I want, all I want to do is change the container file format from MKV to MP4. So re-coding is not necessary. All we need to do is to copy the existing data in the video and audio codecs from the MKV file to the MP4 file thus:


avconv -i input.mkv -vcodec copy -acodec copy output.mp4
or
ffmpeg -i input.mkv -vcodec copy -acodec copy output.mp4


I have also developed the following basic Bash shell script to convert multiple MKV files in a directory. Not the best Bash shell script you will ever see but it works for me, so it is 'good enough'.

mkvTOmp4.sh

#!/bin/bash
#avconv --help
IFS=$'\n' 
clear
for filename in *.mkv
do
echo "$filename"
newfilename="${filename%.mkv}.mp4"
echo "${newfilename}"
avconv -i $filename -vcodec copy -acodec copy $newfilename
done

Note that the code
avconv -i $filename -vcodec copy -acodec copy $newfilename

should all be on one line, but Blogger isn't always co-operating!

I hope someone finds the above MKV to MP4 container file conversion information useful.

Further Reading

Further reading in split into two parts:
  1. Converting from MKV to MP4
  2. Bash shell script tips for multiple conversions.

1. Converting from MKV to MP4

Handbrake settings to convert MKV to MP4 while retaining the original quality notes that If you only want to change the container from MKV to MP4, you don't need to encode anything, you just change the "wrapping" around the video. This doesn't lose quality. You can swap containers easily with FFmpeg – you just have to tell it to copy the video and audio bitstreams:


ffmpeg -i input.mkv -c:v copy -c:a copy output.mp4


Convert mkv to mp4 with ffmpeg notes that

-vcodec copy -acodec copy 
and
-codec copy
or
-c copy
are identical so that

avconv -i input.mkv -c copy output.m4v

can also be used.

How to convert .mkv file into .mp4 file losslessly? has the following suggestions. Changing container without re-enconding content could not be simpler:

avconv -i input.mkv -codec copy output.mp4

It auto-detects a Matroska > MPEG4 container conversion based on input/output filenames. -codec copy tells all content should be copied as-is without re-encoding.

If format auto-detection fails, you can use the -f option before either input, output, or both. From the manual:

f fmt (input/output) forces input or output file format. The format is normally autodetected for input files and guessed from file extension for
output files, so this option is not needed in most cases.

It is worth noting: the above syntax will copy all the files' content to the new container, but some containers may not support some content. So check if your chosen container format, be it mkv, mp4 or even avi has support for all the content in your files (video type, audio type, subtitles, etc). For example, mp4 does not support soft subtitles (.str files)eeded in most cases.

If you want to transfer both the video and audio (both losslessly) and for it not to choke on subtitles then:

avconv -i input.mkv -c:v copy -c:a copy -sn output.mp4

MP4 supports the most of the common audio formats used in MKVs including MP3 and AAC. 

It is also noted that
-codec copy 
means all tracks (formally called "streams", which can be audio, video, subtitles, etc) will be losslessly copied to the new container. So it will change the container from MKV to MP4 without changing the content. 

Avidemux is also recommended for this task. It is noted that it has good GUI interface and that when converting from MKV to MP4 it is not re-encoded. The conversion is done within a minute (for a 60-min video).

sudo apt-get install avidemux

Open the mkv file in avidemux. Select safe mode if prompted. Leave the video as copy. Choose File-> Properties. Check if the audio codec is aac.

If audio codec is NOT aac, select aac(faac). Otherwise, leave it as copy. (Sometimes using this option the audio may be out of sync or distorted, in that case select aac(faac) instead.) Select mp4 for format. Select save, choose location and type a filename with .mp4 as the extension.

The Arista transcoder is also recommended as one option. You can also download various Arista presets from http://www.transcoder.org/presets/


2. Bash Shell Script Tips for Multiple Conversions

There are several things to note about the above shell script mkvTOmp4.sh
  1. IFS=$'\n' 
  2. "${filename%.mkv}.mp4"
  3. for filename in *.mkv












1 comment:

  1. But how do I delete the mkv file after the conversion in script with ffmoeg ?

    ReplyDelete