Realtime transcoding to H264+AAC in Matroska container
Scenario
I have a movie library which I'm looking to make streamable to my Android tablet using VLC for playback. I want to use H264+AAC for best possible visual quality over a mobile internet connection. I also wish to use a Matroska container which supports text subtitles as opposed to bitmap subtitles that MPEG2TS supports.
I have Mediatomb installed and UPnP is working as it should, I can stream video and audio fine without transcoding.
Problem
I can transcode and downsample to MPEG2 just fine. But H264 playback stops after 10-40 seconds on exactly the same place every time for any one video but different places for every video. The playback doesn't even start on VLC on the PC (stops after first frame).
Configs
I'm using this script
#!/bin/bash
LINES=720
PRESET=veryfast
PROFILE=main
TUNE=zerolatency
AUDIO="-c:a libfaac -b:a 128k -ar 48000 -ac 2 -async 1"
# Works well
VIDEO="-c:v mpeg2video -b 8192k"
# Freezes after a few seconds seconds.
#VIDEO="-c:v libx264 -preset ${PRESET} -tune ${TUNE} -profile ${PROFILE}"
SUBTITLES="-c:s copy"
exec /usr/bin/ffmpeg -threads 2 -i "${1}" -filter:v scale=-1:720 $VIDEO \
$AUDIO $SUBTITLES -f matroska -y "${2}" &> /store/tmp/log
My mediatomb config with relevant section:
<profile name="h264stream" enabled="yes" type="external">
<mimetype>video/x-matroska</mimetype>
<accept-url>no</accept-url>
<first-resource>yes</first-resource>
<hide-original-resource>yes</hide-original-resource>
<accept-ogg-theora>yes</accept-ogg-theora>
<sample-frequency>48000</sample-frequency>
<audio-channels>2</audio-channels>
<agent command="/etc/mediatomb/ffmpeg.sh" arguments="%in %out"/>
<buffer size="104857600" chunk-size="262144" fill-size="524288"/>
</profile>
If I do tail -f /store/tmp/log
I can see that the FFMPEG process is still encoding even after the playback stops on the tablet. Infact, it's quite happily chewing away. And it is also encoding at a rate that is faster than the source material so it's not lagging behind. The playback on the tablet is smooth until it suddenly stops.
I've tried using different preset, profile and tune paramters to no avail, the time until it freezes seems to be inversely proportional to how fast the encoder is running (High encoding speed, short time to freeze)
Solution 1:
Adjust the h264 stream to be streamable:
Apparently the h264 codec has a special mode that is required for it to be efficiently streamable and you enable it with: -bsf:v h264_mp4toannexb
Script
The script I use to setup a H264+AAC matroska streaming pipe is this:
#!/bin/bash
# ----------------------------------------------------------------------------
# This script is a helper to transcode a video to H264+AAC with subtitles to a
# Matroska (.mkv) container that is suitable for live streaming to a mobile
# device. It will transcode media that is not H264 or that has too high
# resolution. It will not upsample content.
#
# Other suitable containers (and reasons for not using them) include:
# * ASF (Microsoft, proprietary)
# * MPEG2 Transport Stream (Standard, only supports bitmap subtitles)
# * WebM (Has no support for metadata)
# * DivX (Can't contain H264)
# * FLV (Proprietary Bad support on target device)
# * MP4 (Only bitmap subtitles, didn't work for streaming with FFMPEG)
# * OGG (No support for H264)
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Video options
# ----------------------------------------------------------------------------
LINES=720
# One of: ultrafast,superfast, veryfast, faster, fast, medium, slow, slower,
# veryslow or placebo
PRESET=ultrafast
# One of: baseline, main, high, high10, high422 or high444
PROFILE=high10
# One of: film animation grain stillimage psnr ssim fastdecode zerolatency
TUNE=zerolatency
# ----------------------------------------------------------------------------
# Audio options
# ----------------------------------------------------------------------------
AUDIO="-c:a libfaac -b:a 128k -ar 48000 -ac 2 -async 1"
SUBTITLES="-c:s copy"
# ----------------------------------------------------------------------------
# Read input video parameters
# ----------------------------------------------------------------------------
IN_RESOLUTION=`/usr/bin/ffmpeg -i "${1}" 2>&1 | grep Video | \
perl -lane 'print $1 if /(\d+x\d+)/'`
IN_CODEC=`/usr/bin/ffmpeg -i "${1}" 2>&1 | grep Video | \
perl -lane 'print $1 if /Video: (\S+)/'`
IN_DIMS=(${IN_RESOLUTION//x/ })
V_TRANSCODE="-c:v libx264 -bsf:v h264_mp4toannexb -preset ${PRESET} \
tune ${TUNE} -profile:v ${PROFILE}"
V_COPY="-c:v copy -bsf:v h264_mp4toannexb"
if [ "${IN_DIMS[1]}" > "${LINES}" ]; then
SCALE="-filter:v scale=-1:${LINES} ${OPT_TRANSCODE}"
else
if ["${IN_CODEC}" != "h264" ]; then
VIDEO=$OPT_TRANSCODE
else
VIDEO=$V_COPY
fi
fi
exec /usr/bin/ffmpeg -threads `nproc` -i "${1}" $VIDEO $AUDIO $SUBTITLES \
-f matroska -y "${2}" &> /store/tmp/log
TODO:
Make it read subtitles from external files if found and add them to the matroska stream. Make it not transcode the audio stream if it is already in a suitable format.