summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard <richard@ha-server.local>2016-09-04 11:27:10 +0100
committerRichard <richard@ha-server.local>2016-09-04 11:27:10 +0100
commitdb51d3bbfdd63d4c62ca718bfcd7467d2cdc6fa6 (patch)
tree0a2ada6f32615a2b4f7270b8d2a3e203bff3c1c5
parent83632745c43d406856a3b9948cb5ea4a5ec22666 (diff)
downloadvdr-convert-db51d3bbfdd63d4c62ca718bfcd7467d2cdc6fa6.tar.gz
vdr-convert-db51d3bbfdd63d4c62ca718bfcd7467d2cdc6fa6.tar.bz2
Files not symlinks
-rw-r--r--[l---------]FFMPEG patches/FFMPEG-libavformat-reduce-exits-on-subs-error.patch20
-rw-r--r--[l---------]VDR patches/VDR2.20-increase-frame-detector-for-x264.patch13
-rwxr-xr-x[l---------]VDR patches/VDR2.20-proposal-to-fix-index-generation-for-radio-recording.patch91
-rwxr-xr-x[l---------]batch.sh59
-rwxr-xr-x[l---------]vdr-auto43
-rwxr-xr-x[l---------]vdr-convert924
6 files changed, 1144 insertions, 6 deletions
diff --git a/FFMPEG patches/FFMPEG-libavformat-reduce-exits-on-subs-error.patch b/FFMPEG patches/FFMPEG-libavformat-reduce-exits-on-subs-error.patch
index 00ad9f1..a6fd2f0 120000..100644
--- a/FFMPEG patches/FFMPEG-libavformat-reduce-exits-on-subs-error.patch
+++ b/FFMPEG patches/FFMPEG-libavformat-reduce-exits-on-subs-error.patch
@@ -1 +1,19 @@
-/opt/data/develop/FFmpeg-rf/FFMPEG-libavformat-reduce-exits-on-subs-error.patch \ No newline at end of file
+--- mux.c 2016-09-01 14:21:01.254840644 +0100
++++ new/mux.c 2016-09-01 14:18:01.000000000 +0100
+@@ -597,11 +597,11 @@
+ pkt->dts = st->pts_buffer[0];
+ }
+
+- if (st->cur_dts && st->cur_dts != AV_NOPTS_VALUE &&
+- ((!(s->oformat->flags & AVFMT_TS_NONSTRICT) &&
+- st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE &&
+- st->codecpar->codec_type != AVMEDIA_TYPE_DATA &&
+- st->cur_dts >= pkt->dts) || st->cur_dts > pkt->dts)) {
++// RF This patch helps prevent ffmpeg exiting unecessarily due to DTS issues in subtitles.
++// My idea is that DTS issues don't matter for subs, they only use PTS. In any case timing not mega critical.
++
++// if (st->cur_dts && st->cur_dts != AV_NOPTS_VALUE && ((!(s->oformat->flags & AVFMT_TS_NONSTRICT) && st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE && st->codecpar->codec_type != AVMEDIA_TYPE_DATA && st->cur_dts >= pkt->dts) || st->cur_dts > pkt->dts)) {
++ if (st->cur_dts && st->cur_dts != AV_NOPTS_VALUE && ((!(s->oformat->flags & AVFMT_TS_NONSTRICT) && st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE && st->codecpar->codec_type != AVMEDIA_TYPE_DATA && st->cur_dts >= pkt->dts) || (st->cur_dts > pkt->dts && st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE && st->codecpar->codec_type != AVMEDIA_TYPE_DATA))) {
+ av_log(s, AV_LOG_ERROR,
+ "Application provided invalid, non monotonically increasing dts to muxer in stream %d: %s >= %s\n",
+ st->index, av_ts2str(st->cur_dts), av_ts2str(pkt->dts));
diff --git a/VDR patches/VDR2.20-increase-frame-detector-for-x264.patch b/VDR patches/VDR2.20-increase-frame-detector-for-x264.patch
index 4021e2c..8315d28 120000..100644
--- a/VDR patches/VDR2.20-increase-frame-detector-for-x264.patch
+++ b/VDR patches/VDR2.20-increase-frame-detector-for-x264.patch
@@ -1 +1,12 @@
-/opt/data/develop/vdr/vdr-220/patches/VDR2.20-increase-frame-detector-for-x264.patch \ No newline at end of file
+--- remux.c 2016-09-01 14:53:42.969255952 +0100
++++ new/remux.c 2016-06-30 17:06:06.000000000 +0100
+@@ -23,7 +23,8 @@
+ #define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a)
+ #define dbgframes(a...) if (DebugFrames) fprintf(stderr, a)
+
+-#define MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION 6
++//RF fix for x264 long commnd line header #define MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION 6
++#define MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION 10
+ #define WRN_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION (MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION / 2)
+ #define WRN_TS_PACKETS_FOR_FRAME_DETECTOR (MIN_TS_PACKETS_FOR_FRAME_DETECTOR / 2)
+
diff --git a/VDR patches/VDR2.20-proposal-to-fix-index-generation-for-radio-recording.patch b/VDR patches/VDR2.20-proposal-to-fix-index-generation-for-radio-recording.patch
index a516d15..7cdc80e 120000..100755
--- a/VDR patches/VDR2.20-proposal-to-fix-index-generation-for-radio-recording.patch
+++ b/VDR patches/VDR2.20-proposal-to-fix-index-generation-for-radio-recording.patch
@@ -1 +1,90 @@
-/opt/data/develop/vdr/vdr-220/patches/VDR2.20-proposal-to-fix-index-generation-for-radio-recording.patch \ No newline at end of file
+>From ecfdf422cb4d7e88088cfb740197e684adb69966 Mon Sep 17 00:00:00 2001
+From: Thomas Reufer <thomas@reufer.ch>
+Date: Mon, 13 Jun 2016 16:42:03 +0200
+Subject: [PATCH 9/9] proposal to fix index generation for radio recordings
+
+---
+ recording.c | 8 ++++----
+ remux.c | 4 +++-
+ remux.h | 3 +++
+ 3 files changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/recording.c b/recording.c
+index a847c7d..b1b7c81 100644
+--- a/recording.c
++++ b/recording.c
+@@ -2324,7 +2324,7 @@ void cIndexFileGenerator::Action(void)
+ Buffer.Del(Processed);
+ }
+ }
+- else if (PatPmtParser.Vpid()) {
++ else if (PatPmtParser.Completed()) {
+ // Step 2 - sync FrameDetector:
+ int Processed = FrameDetector.Analyze(Data, Length);
+ if (Processed > 0) {
+@@ -2346,9 +2346,9 @@ void cIndexFileGenerator::Action(void)
+ PatPmtParser.ParsePmt(p, TS_SIZE);
+ Length -= TS_SIZE;
+ p += TS_SIZE;
+- if (PatPmtParser.Vpid()) {
+- // Found Vpid, so rewind to sync FrameDetector:
+- FrameDetector.SetPid(PatPmtParser.Vpid(), PatPmtParser.Vtype());
++ if (PatPmtParser.Completed()) {
++ // Found pid, so rewind to sync FrameDetector:
++ FrameDetector.SetPid(PatPmtParser.Vpid() ? PatPmtParser.Vpid() : PatPmtParser.Apid(0), PatPmtParser.Vpid() ? PatPmtParser.Vtype() : PatPmtParser.Atype(0));
+ BufferChunks = IFG_BUFFER_SIZE;
+ Rewind = true;
+ break;
+diff --git a/remux.c b/remux.c
+index cfb6ae3..df38add 100644
+--- a/remux.c
++++ b/remux.c
+@@ -603,6 +603,7 @@ cPatPmtParser::cPatPmtParser(bool UpdatePrimaryDevice)
+
+ void cPatPmtParser::Reset(void)
+ {
++ completed = false;
+ pmtSize = 0;
+ patVersion = pmtVersion = -1;
+ pmtPids[0] = 0;
+@@ -893,6 +894,7 @@ void cPatPmtParser::ParsePmt(const uchar *Data, int Length)
+ }
+ }
+ pmtVersion = Pmt.getVersionNumber();
++ completed = true;
+ }
+ else
+ esyslog("ERROR: can't parse PMT");
+@@ -1541,7 +1543,7 @@ void cFrameDetector::SetPid(int Pid, int Type)
+ parser = new cH264Parser;
+ else if (type == 0x24)
+ parser = new cH265Parser;
+- else if (type == 0x04 || type == 0x06) // MPEG audio or AC3 audio
++ else if (type == 0x03 || type == 0x04 || type == 0x06 || type == 0x0F || type == 0x11) // MPEG2,4 AAC, AC3 audio
+ parser = new cAudioParser;
+ else if (type != 0)
+ esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid);
+diff --git a/remux.h b/remux.h
+index bd0d145..80528bd 100644
+--- a/remux.h
++++ b/remux.h
+@@ -361,6 +361,7 @@ private:
+ uint16_t compositionPageIds[MAXSPIDS];
+ uint16_t ancillaryPageIds[MAXSPIDS];
+ bool updatePrimaryDevice;
++ bool completed;
+ protected:
+ int SectionLength(const uchar *Data, int Length) { return (Length >= 3) ? ((int(Data[1]) & 0x0F) << 8)| Data[2] : 0; }
+ public:
+@@ -397,6 +398,8 @@ public:
+ int Vtype(void) const { return vtype; }
+ ///< Returns the video stream type as defined by the current PMT, or 0 if no video
+ ///< stream type has been detected, yet.
++ bool Completed(void) { return completed; }
++ ///< Returns true if the PMT has been completely parsed
+ const int *Apids(void) const { return apids; }
+ const int *Dpids(void) const { return dpids; }
+ const int *Spids(void) const { return spids; }
+--
+2.4.9
+
diff --git a/batch.sh b/batch.sh
index 1d13ce5..4d1f5c2 120000..100755
--- a/batch.sh
+++ b/batch.sh
@@ -1 +1,58 @@
-/opt/data/develop/scripts/batch.sh \ No newline at end of file
+#!/bin/bash
+# Shell script to convert VDR recordings in todo.txt to mpeg4 files. No upload
+#------------------------------------------------------------------------------
+# Revision History
+#------------------------------------------------------------------------------
+#
+# $Log: batch.sh,v $
+# Revision 1.3 2016/09/01 13:08:06 richard
+# Pass multiple vdr-convert flags
+# Update VDR afterwards
+#
+# Revision 1.2 2014/12/11 15:24:41 richard
+# Improvements
+#
+# Revision 1.1 2014/12/10 10:44:19 richard
+# Initial import - as used in BKK 2014
+#
+#------------------------------------------------------------------------------
+
+# Usual way to create a "todo" list is as follows:
+# find $DIR -type d | grep -i '.rec$' > $DIR/todo.txt
+
+#Default directory where we work
+input='/mnt/lvm/TV'
+root=$input
+
+function usage {
+ echo "usage: $0 <vdr-convert args>"
+}
+
+function logit() {
+ logger -s -p local2.warn -t batch "$1"
+}
+
+#------------------------------------------------------------------------------
+# Start of script
+#------------------------------------------------------------------------------
+
+[ $# -eq 0 ] && usage && exit 1
+
+for arg in "$@";
+do
+ args="$args '$arg'"
+done
+
+# Timeout req'd for troublesome conversions where ffmpeg sometimes get stuck.
+# (NOTE Causes issues when run interactively.)
+NAMES="$(< $(pwd)/todo.txt)" #names from todo.txt file in this directory
+for NAME in $NAMES; do
+# /bin/su vdr -c vdr-convert -i "\"$NAME"\" $args"
+ timeout -k 5h 4h sh -c "vdr-convert -i "\"$NAME"\" $args"
+ [ $? -ne 0 ] && logit "Fail: problem converting $NAME"
+done
+
+#Ask VDR to re-read the files
+touch "$root/.update"
+
+# --------- $Id: batch.sh,v 1.3 2016/09/01 13:08:06 richard Exp $ ---------- END
diff --git a/vdr-auto b/vdr-auto
index 2325c68..d2946a3 120000..100755
--- a/vdr-auto
+++ b/vdr-auto
@@ -1 +1,42 @@
-/opt/data/develop/scripts/vdr-auto \ No newline at end of file
+#!/bin/bash
+# Shell script to handle VDR post-recording actions, like noad, H264 conversions
+# Based on VDR example
+#------------------------------------------------------------------------------
+# Revision History
+#
+# Removed --asd as it was moving the cut marks to the wrong places
+# Removd -C (scene change detection) which seems to mess up marks unecessarily
+#------------------------------------------------------------------------------
+#
+# $Log: vdr-auto,v $
+# Revision 1.1 2016/09/01 12:35:23 richard
+# Initial import
+#
+#------------------------------------------------------------------------------
+
+case "$1" in
+ before)
+ echo "Before recording $2"
+ ;;
+ started)
+ echo "Started recording $2"
+ /usr/local/bin/noad -s /etc/vdr/noadstat.csv -o -c $1 $2
+ ;;
+ after)
+ echo "After recording $2"
+ #vdr forks this script, noad forks itself, vdr-convert doesn't
+ /usr/local/bin/noad -s /etc/vdr/noadstat.csv -o -c $1 $2
+ /usr/local/bin/vdr-convert -k -d -i $2 &
+ ;;
+ edited)
+ echo "Edited recording $2"
+ ;;
+ deleted)
+ echo "Deleted recording $2"
+ ;;
+ *)
+ echo "ERROR: unknown state: $1"
+ ;;
+esac
+
+# --------- $Id: vdr-auto,v 1.1 2016/09/01 12:35:23 richard Exp $ ---------- END \ No newline at end of file
diff --git a/vdr-convert b/vdr-convert
index 04a323e..0c6990a 120000..100755
--- a/vdr-convert
+++ b/vdr-convert
@@ -1 +1,923 @@
-/opt/data/develop/scripts/vdr-convert \ No newline at end of file
+#!/bin/bash
+
+# VDR-convert Copyright (C) 2014-2016 Richard Farthing
+
+# A shell script to convert VDR recordings to ts using H264 and AAC
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Note that this script is designed for VDR format 1.x and 2.x recordings only
+#
+# The aim is to re-create the content as accurately as possible, including all
+# streams, audio, AD and subtitles but in more compressed content
+#
+# For VDR conversion/compatibility we use MPEG Transport Streams (ts).
+# Previous versions used Matroska (MKV) for single-use conversions,
+# but overly complex to maintain with no huge benefit (marginal size reduction)
+# Some commented-out historical lines remain for HandbrakeCLI.
+#
+# Extract of VDR V1.7.3 history - naming conventions
+#The directory name for a recording has been changed from
+#YYYY-MM-DD-hh[.:]mm.pr.lt.rec (pr=priority, lt=lifetime) to
+#YYYY-MM-DD-hh.mm.ch-ri.rec (ch=channel, ri=resumeId).
+#Priority and Lifetime are now stored in the info.vdr file with the new
+#tags P and L (if no such file exists, the maximum values are assumed by
+#default, which avoids inadvertently deleting a recording if disk space
+#is low). No longer storing Priority and Lifetime in the directory name
+#avoids starting a new recording if one of these is changed in the timer
+#and the recording is re-started for some reason.
+#Instead of Priority and Lifetime, the directory name now contains the
+#channel number from which the recording was made, and the "resume id" of
+#this instance of VDR.
+#
+#------------------------------------------------------------------------------
+# Compatibility and requirements:
+#------------------------------------------------------------------------------
+# VDR 1.x - 2.2x+ recordings in PES or TS format with "info" files
+# Obviously script needs write access to VDR recordings (run as vdr?).
+#
+# Software required
+# -----------------
+# Current released ffmpeg in the path (3.x tested)
+# Or a patched ffmpeg for damaged (bad reception) recordings or if you want
+# the ultimate quality AAC using the non-free libfdk, build ffmpeg with it
+#
+# For transcoding old VDR1.x recordings to the newer .ts format in H264, and for
+# portability of subtitles etc, you need the following also, in your path:
+# 1) ISO13818ts from http://www.scara.com/~schirmer/o/mplex13818 (build from source)
+# 2) Genindex 0.2 or later (part of this project)
+# 3) For auto-uploading to an ftp server: Ncftp. Adavantage of this is it can resume
+# in case the upload breaks midway. Auto-resume not always reliable: hence "redo" opt
+#
+# The script works around various issues with existing VDR recordings
+# esp. packet timings, PTS and DTS timestamp values. However if your recordings
+# are really broken, you are first likely to see failures in the subtitle tests (map fn.)
+# You can either:
+# a) use -n flag and discard subs, OR
+# b) Patch ffmpeg as specified in the README, and let ffmpeg work through.
+# Set the ffmpeg variable below to point to your patched version
+# In reality this is often produces perfect results, inc subs.
+#
+#------------------------------------------------------------------------------
+# Revision History
+#------------------------------------------------------------------------------
+#
+# $Log: vdr-convert,v $
+# Revision 1.3 2016/09/01 13:01:48 richard
+# Added support for VDR1.x files
+# Added extensive support for dvb subtitles
+# Added options c, d, e, l, m, q, t, y
+# Added in-place conversion for "keeping" (-k)
+# Added tests for size, duration, indexing
+# Added workarounds for poor recordings/ffmpeg
+# Added lockfile to prevent multiple invocations
+# All streams labelled using data from info(.vdr) file
+# Stream re-ordering to accomodate typ. players
+# Extensive testing on 8yr libary of recordings
+# Removed most mkv/mkvmerge/HandbrakeCLI code
+#
+# Revision 1.2 2014/12/12 12:35:31 richard
+# Support VDR2 and ts files
+# Support and tag HD/SD
+# Fix audio encoding parameters
+# Code tidy
+#
+# Revision 1.1 2014/12/10 10:44:19 richard
+# Initial import - as used in BKK 2014
+#------------------------------------------------------------------------------
+
+# ********************************* TODO >> **********************************
+#
+# Logging criticality - all same atm
+# Paths for executables, used as variables, test for their existance
+# mplayer plays at 2x speed (confuses fps, -fps 25 doesn't help)
+# Automatic test/retry FTP upload size to fix upload failures
+#
+# ffmpeg bug (for VDR1.x subs) https://trac.ffmpeg.org/ticket/4855
+# FFmpeg bug (early exit on all types of recordings) https://trac.ffmpeg.org/ticket/5617
+# FFmpeg bug (some subtitles not handled) https://trac.ffmpeg.org/ticket/5796
+#
+# ********************************* << TODO ***********************************
+
+# CONSTANTS / USER CONFIG
+
+#This is for detailed ffmpeg output
+LOGFILE="/var/log/vdr-convert.log"
+#ffmpeg="timeout -k 5h 4h nice -n 19 /opt/data/develop/FFmpeg/ffmpeg -y -hide_banner -nostats -probesize 250M -analyzeduration 600M -copytb 1"
+# Timeout req'd for troublesome conversions where ffmpeg can (rarely) get stuck. However causes issues when run interactively.
+ffmpeg="nice -n 19 /opt/data/develop/FFmpeg/ffmpeg -y -hide_banner -nostats -probesize 250M -analyzeduration 600M -copytb 1"
+
+#Minimum file we bother to convert
+#Note esp. that small mkv files often fail to concatenate in mkvmerge
+minsize='2000000'
+
+# libfdk is supposed to be better but seems to produce larger files and
+# doesn't subjectively (to me) sound better. It's a bit faster to encode
+aaccodec='aac' # aac or libfdk_aac if ffmpeg distribution / built GPL
+
+minsubsize=250 # kb, a subs stream smaller than this probably broken or just a few in the ads
+ # Also broadcaster "cleardown" - essentially empty - packets contribute
+ # This is a default, recording duration is used to calculate more accurately
+
+# Defaults in case not provided by info file
+Alangstereo="eng"
+Alangmono="eng"
+Alangmulti="eng"
+Alangsurround="eng"
+ADlang="eng"
+Slang="eng"
+ext="ts" # target file extension
+
+# ********************* Should not need to edit below here ********************
+
+lifetime=99
+meg=1048576
+# Catch piped errors
+set -o pipefail
+# New line
+IFS=$'\n'
+
+#------------------------------------------------------------------------------
+# FUNCTIONS
+#------------------------------------------------------------------------------
+
+function quit() {
+ rm -f "$LOCKFILE" || { logit "failed to delete $LOCKFILE"; exit 1; }
+ exit $1
+}
+
+#------------------------------------------------------------------------------
+function logit() {
+ logger -s -p local2.warn -t vdr-convert "$1"
+}
+
+#------------------------------------------------------------------------------
+# Must analyse separately as wrong duration returned once -copyts used (for subs)
+
+function progduration {
+ if [ $debug -eq 1 ]; then
+ duration=$(echo $debug_time|cut -f2 -d"t" | awk -F: '{ printf "%0.f", ($1 * 3600) + ($2 * 60) + $3 }')
+ else
+ duration=$(eval "$ffmpeg -i "\"$1"\" 2>&1" |grep -i "duration:" |cut -f2-5 -d":" | awk -F: '{ printf "%0.f", ($1 * 3600) + ($2 * 60) + $3 }')
+ fi
+ if [ $duration ]; then
+ echo;echo "Duration is $duration seconds"
+ minsubsize=$(($duration / 9)) # 400k / hr absolute min (empirical)
+ fi
+}
+
+#------------------------------------------------------------------------------
+# Analyse streams
+# Handbrake starts at 1. We take the first / best stream, ac3 then stereo etc
+# (we assume only 1 present)
+# Handbrake is variable around audio channels
+# cant ask for 1,2 unless both there as it barfs, (or did do)
+
+# Concatenating AAC at stream level has resulted in lipsync issues, do file level
+#------------------------------------------------------------------------------
+
+function map() {
+# $1 is filename, $2 is filename count
+
+ declare -a VIDEO='()'
+ declare -a AUDIO='()'
+ declare -a SUBS='()'
+ F_VIDEO=""
+ F_AUDIO=""
+ F_SUBS=""
+ H_AUDIO_MAP=""
+ H_AUDIO_CODEC=""
+ H_AUDIO_QUALITY=""
+ H_SUBS=""
+ h264=0
+ aac=0
+
+# Multichannel recordings often start in stereo, e.g. in a continuity announcement
+# This trick gets the ONE "best" audio stream say 5 mins in to check what it really is
+# ffmpeg -loglevel quiet -ss 00:05:00 -i "00001.ts" -t 10 -c copy -vn -avoid_negative_ts 1 -f nut pipe:1 | ffmpeg -hide_banner -i pipe:0
+# Also AD steam contents often missing in continuity (empty).
+# So a) check after 5 mins for 5.1, and b) probe well into the file for streams not yet running - like AD
+# bestaudio=`ffmpeg -loglevel quiet -probesize 200M -analyzeduration 200M -ss 00:05:00 -i "$1" -t 10 -c copy -vn -avoid_negative_ts 1 -f nut pipe:1 | ffmpeg -hide_banner -i pipe:0 2>&1 |grep -i "stream #0:"`
+# echo "ffmpeg best audio is:"
+# echo "$bestaudio"
+
+ mapst=""
+ echo "ffmpeg stream data:"
+ streams=$(eval "$ffmpeg -i "\"$1"\" 2>&1" |grep -i "stream #0:") # Line per stream
+ echo "$streams"
+ for line in $streams;
+ do
+ stream_id=$(echo $line |cut -f2 -d":" |cut -f1 -d"[" |sed 's/[^0-9]*//g')
+ bitrate=$(echo ${line##*,} | sed 's/[^0-9]*//g')
+ ! [ $(echo $bitrate | sed 's/[^0-9]*//g') ] && bitrate=0 # always need a number
+ if [ $(echo $line |grep -i "video") ]; then
+ mapst="$mapst A:$((10000 - $bitrate)):$stream_id" # The 'A' character & bitrate is used as a score for sorting streams later
+
+ [ $(echo $line |grep -i "h264") ] && h264=1
+
+ # Notes on ffmpeg video parameters:
+ # ---------------------------------
+ # Assumption: anything worth playing video today will handle H264 high profile 4.0, including Raspberry Pi.
+ # (also Freeview HD mandates it, making it a pre-requisite)
+
+ # yadif/bwdif deinterlace option (if interlaced) as we see interlacing crap on fast movements using Kodi or VLC
+ # NOTE that yadif/bwdif outputs 1 frame per *field* (send_field=1) = 50fps PAL when set to 1.
+ # Larger file but can be better esp. on mixed old/new material. Or just leave interlaced and let player deinterlace
+
+ # Crop option is to remove overscan crap that the broadcaster forgot to filter at the top of the picture
+ # (2-4 pixels often, +check for MPEG artifacts adjacent left over).
+
+ # -g (max keyframe gap) adjusted to improve skipping which is poor at the ffmpeg default 250.
+ # -g 75 Adds about 2% to filesize, -g 50 adds ~3.5%. 12 is common in broadcast!
+ # (See http://www.lighterra.com/papers/videoencodingh264)
+
+ # Can also transcode to H265, but v. slow (<real time on dual core) and only about 20% space savings over H264,
+ # + RPi v1 cannot handle
+
+ [ $top -gt 0 ] && crop="-vf crop=in_w:in_h-$top:0:$top"
+ if [ $dif ]; then
+ flags="+loop"
+ deint="-vf yadif=$dif:-1:0"
+ else
+ flags="+loop+ilme+ildct"
+ fi
+ VIDEO[$stream_id]="-c:v:stream_op libx264 -preset $preset -profile:v high -level 4.0 -crf $quality -g 50 -flags $flags $extras $deint $crop"
+ # No deinterlace normally req'd.
+ [ $mpeg -eq 1 ] && VIDEO[$stream_id]="-c:v:stream_op copy -g 50 $extras $deint $crop"
+# v. quick functional testing only
+# F_VIDEO="-c:v libx264 -preset ultrafast -vf scale=112:63"
+# F_VIDEO="-c:v mpeg2video"
+ # HandBrake params are less tested as it is no longer the chosen encoder. Added decomb for interlacing issues
+ H_VIDEO="-e x264 --decomb --crop $top:0:0:0 --strict-anamorphic -m -x ref=1:weightp=1:subq=2:rc-lookahead=20:trellis=0:8x8dct=0 -q $quality"
+ fi
+ if [ $(echo $line |grep -i "audio") ] && [ $mpeg -eq 1 ]; then
+ AUDIO[$stream_id]="-c:a:stream_op copy -metadata:s:a:stream_op language=$Alangstereo"
+ mapst="$mapst B:$((10000 - $bitrate)):$stream_id"
+ H_AUDIO_MAP="$H_AUDIO_MAP $stream_id,"
+ H_AUDIO_CODEC="$H_AUDIO_CODEC copy:*,"
+ # Multichannel audio - just copy to best preserve. Can be AAC 5.1. Always put multichannel ahead of others
+ elif [ $(echo $line |grep -i "audio" |grep -i "ac3\|5.1") ]; then
+ mapst="$mapst C:$((10000 - $bitrate)):$stream_id"
+ H_AUDIO_MAP="$H_AUDIO_MAP $stream_id,"
+ H_AUDIO_CODEC="$H_AUDIO_CODEC copy:*,"
+ AUDIO[$stream_id]="-c:a:stream_op copy -metadata:s:a:stream_op language=$Alangsurround"
+ # AAC best left as is. We can't always detect AAC 5.1, so just copy AAC as well
+ elif [ $(echo $line |grep -i "audio" |grep -i "aac") ]; then
+# H_AUDIO="-a $STREAM -E av_aac -B $multibitrate"
+ mapst="$mapst E:$((10000 - $bitrate)):$stream_id"
+ H_AUDIO_MAP="$H_AUDIO_MAP $stream_id,"
+ H_AUDIO_CODEC="$H_AUDIO_CODEC copy:*,"
+ AUDIO[$stream_id]="-c:a:stream_op copy -metadata:s:a:stream_op language=$Alangstereo"
+ aac=1
+ else
+ # MP2 usually. ffmpeg sometimes says "2 channels".
+ # AD can actually be stereo, esp in HD, or "0 channels" - a null stream, which we drop
+ if [ $(echo $line |grep -i "audio" |grep -i "stereo\|2 channels") ]; then
+ # sometimes the bitrate is already lower than our target, like 128k! AAC good at low rates anyway
+ outputbitrate=$stereobitrate
+ mapst="$mapst G:$((10000 - $bitrate)):$stream_id"
+ (( bitrate = bitrate * 10 )) # AKA 192k -> 160k
+ (( bitrate = bitrate / 12 ))
+ if [ $outputbitrate -gt $bitrate ]; then
+ outputbitrate=$bitrate
+ [ $outputbitrate -lt 100 ] && logit "Fail: Audio bitrate too low ($outputbitrate), exiting" && quit 1
+ fi
+ H_AUDIO_MAP="$H_AUDIO_MAP $stream_id,"
+ H_AUDIO_CODEC="$H_AUDIO_CODEC mp3,"
+ H_AUDIO_QUALITY="$H_AUDIO_QUALITY $stereoquality,"
+# AAC now reported as OK CBR, Dec 2015. Smaller files too (than mp3)
+ AUDIO[$stream_id]="-c:a:stream_op $aaccodec -b:a:stream_op $outputbitrate"k" -metadata:s:a:stream_op language=$Alangstereo"
+ elif [ $(echo $line |grep -i "audio" |grep -i "mono") ]; then
+ # Likely the AD stream, at low bitrate.
+ # !!!AAC good at low bitrates. But sparse audio gives Kodi/VLC etc trouble, as does MP3 - so copy MP2: 0.5% hit approx!
+ mapst="$mapst J:$((10000 - $bitrate)):$stream_id"
+ H_AUDIO_MAP="$H_AUDIO_MAP $stream_id,"
+ H_AUDIO_CODEC="$H_AUDIO_CODEC mp3,"
+ # can't easily mix quality and bitrate in Handbrake cli, so use stereo
+ H_AUDIO_QUALITY="$H_AUDIO_QUALITY $stereoquality,"
+ AUDIO[$stream_id]="-c:a:stream_op copy -metadata:s:a:stream_op language=$ADlang"
+ fi
+ fi
+ if [ $subs -eq 1 ] && [ $(echo $line |grep -i "dvb_subtitle") ]; then
+ # Broken subs streams WILL cause an early exit, so test them quickly : both ability to copy & their size
+ # (Skip any dvd_sub misidentified in old VDR recordings - e.g. if .vdr files passed unprocessed)
+ # Can be multiple streams.
+ echo;echo "Testing subtitles for problems..."
+ substest=$(eval "$ffmpeg -i "\"$1"\" -vn -an -dn -c:s copy -f mpegts /dev/null 2>&1")
+ [ $? -ne 0 ] && logit "ffmpeg failed subtitles stream test" && quit 1
+ subslen=$(echo "$substest" |grep "subtitle:"|cut -f4 -d":"|sed 's/[^0-9]*//g')
+ # arbitrary size check, but generally produces little if broken
+ if [ $subslen -ge $minsubsize ]; then
+ echo "Subs length $subslen kbytes OK"
+ mapst="$mapst L:10000:$stream_id"
+ H_SUBS="$H_SUBS -s $stream_id"
+ SUBS[$stream_id]="-c:s:stream_op copy -metadata:s:s:stream_op language=$Slang"
+ # Unsure if forcing dvbsub causes trouble. Usually unhelpful: results in different size o/p
+ # SUBS[$stream_id]="-c:s:stream_op dvbsub -metadata:s:s:stream_op language=$Slang"
+ else
+ logit "Warning ffmpeg failed subtitles stream in $1: too small ($subslen kb), none or broken: no subtitles output"
+ # If VDR2, perhaps not possible to transcode with subs. VDR1.x size already assessed, shouldn't get here
+ [ ! $VDRtype ] && [ $delete -eq 1 ] && delete=0 && logit "No longer deleting originals for $1 due to suspect subtitle stream size"
+ fi
+ fi
+ done
+
+ # Handbrake needs parameters listed like this - per stream
+ H_AUDIO="-a $H_AUDIO_MAP -E $H_AUDIO_CODEC -Q $H_AUDIO_QUALITY"
+
+ # Order streams by initial alpha AND bitrate to fix stream order change by iso13818ts, or random broadcaster ordering
+ # (many players play the first audio stream without user intervention). Produces "best" audio first, a bit like ffmpeg
+ # Note that stream ordering (as reported by ffmpeg) has been observed varying between recording files in same recording!
+ [ $debug -eq 1 ] && echo;echo "Raw map string is: $mapst"
+ saveifs=$IFS
+ IFS=' '
+ totalmap=$(echo "$mapst"|tr " " "\n"|sort|cut -f1,3 -d":"|tr "\n" " "|tr "[:alpha:]" "$2")
+ mapping=""
+ vcount=0; acount=0; scount=0
+
+ # Build the OUTPUT maps (order of outputs)
+ for m in $totalmap;
+ do
+ mapping="$mapping -map $m"
+ id=$(echo $m|sed -e 's/[0-9]://g')
+ [ "${VIDEO[$id]}" ] && { F_VIDEO="$F_VIDEO $(echo "${VIDEO[$id]}" |sed -e "s/stream_op/$vcount/g")";vcount=$(($vcount+1));}
+ [ "${AUDIO[$id]}" ] && { F_AUDIO="$F_AUDIO $(echo "${AUDIO[$id]}" |sed -e "s/stream_op/$acount/g")";acount=$(($acount+1));}
+ #iff subs (enabled and actually present) we need -copyts for proper sync
+ [ "${SUBS[$id]}" ] && { F_SUBS="$F_SUBS -copyts $(echo "${SUBS[$id]}" |sed -e "s/stream_op/$scount/g")";scount=$(($scount+1));}
+ done
+ IFS=$saveifs
+
+ map="$map $mapping" # Additive complete map where needed
+ echo;echo "stream mapping for $1 is:"
+ echo "$map"
+ [ $debug -eq 1 ] && echo "$F_VIDEO $F_AUDIO $F_SUBS"
+ return 0
+}
+
+#------------------------------------------------------------------------------
+
+function convertvdr() {
+ # $1 is input filename, $2 is output path/filename
+ # returns 0 on success, 1 on fail, 2 if already in H264/AAC
+ # Note always need to probe as deeply else ffmpeg can miss/fail, e.g AD streams/subs
+ main=$1
+ tempdir=$(echo "$(dirname "$1")""/temp")
+
+ if [ $VDRtype ]; then
+ # The tricky bit - getting old VDR 1.x files into a format that ffmpeg can transcode, esp. non-std dvbsubs
+ # This preprocessing is v. quick, really only limited by disk accesss time, as it's just stream copying & manipulation
+ # -copyts is CRITICAL to getting the timing of the final subs streams aligned properly, as opposed to approximately!
+ # genindex always creates 001.vdr + index.vdr
+
+ # NOTE an earlier version made the ts's (disk intensive) only when subs detected by genindex
+ # but ffmpeg occasionally fails detection of *any* streams when given an unprocessed .vdr file
+ rm -f "$tempdir"/*.vdr # in case we're re-doing: genindex otherwise fails
+ mkdir -p -- "$tempdir"
+ [ $? -ne 0 ] && logit "Failed - cannot create working directory $tempdir" && return 1
+ subxsize=$(genindex -n -b -i "$1" -d "$tempdir" |grep -i "^PES: Stats" |grep -i "subtitle data" |cut -f2 -d":"|sed 's/[^0-9]*//g')
+ if [ $? -ne 0 ]; then
+ logit "Fail: genindex problem converting $1"
+ [ $debug -eq 0 ] && rm -rf "$tempdir" || logit "could not remove $tempdir"
+ return 1
+ else
+ echo
+ logit "$1 genindex extracted $subxsize kb subtitle data"
+ # convert to ffmpeg-compatible .ts (so ffmpeg can see the streams in an mpegts container - no dvb decoder for PS/PES)
+ # NOTE that iso13818ts oftens changes the order of streams which may be relevant for audio playback - see map fn.
+ # Fix this later when reassembling streams
+ tmpts="$tempdir/001.ts"
+ nice -n 19 iso13818ts -P "$tempdir/001.vdr" 1 > "$tmpts"
+ if [ $? -ne 0 ]; then
+ logit "Fail: Problem converting $tempdir/001.vdr to mpegts, deleting $tmpts !"
+ [ $debug -eq 0 ] && rm -f $tmpts || logit "could not remove $tmpts"
+ return 1
+ fi
+ [ $debug -eq 0 ] && rm -f "$tempdir"/*.vdr || logit "could not remove temp files" # No longer reqd
+
+ progduration $tmpts
+ program_duration=$(($program_duration + $duration)) # combine all files. Must test as .ts, .vdr can fail
+
+ # Subs stream
+ if [ $subs -eq 1 ]; then
+ if [ $subxsize -gt $minsubsize ]; then
+ # Subs streams: MUST DO SEPARATELY as ffmpeg has big problems extracting ALL the subs and AD sparse packets
+ # (cannot currently convert a small filtered subs-only file - doesn't work)
+ # Many players also have this problem - esp if the video stream is present.
+ # After exhaustive testing with many flags / options, atm there's NO other way to avoid this and reliably extract ALL dvbsubs
+ subsfile="$tempdir/subs.ts"
+ cmd="$ffmpeg -copyts -i "\"$tmpts"\" -c:s copy -vn -an -dn "\"$subsfile"\""
+ sh -c $cmd
+ if [ $? -ne 0 ]; then
+ logit "Fail: ffmpeg problem extracting subs stream(s) of $tmpts, deleting $subsfile (check $LOGFILE)"
+ [ $debug -eq 0 ] && rm -f "$subsfile" || logit "could not remove $subsfile"
+ return 1
+ fi
+
+ # add and map the extra subs file, so the main conversion catches it, check ffmpeg didn't drop more than 10kb subs (~1 sub)
+ map $subsfile 1
+ [ $subxsize -gt $(($subslen+10)) ] && logit "Fail: ffmpeg did not process all subs for $1 (VDR 1.x, $subslen kb processed, $subxsize kb extracted), deleting $tempdir !" && rm -rf "$tempdir" && return 1
+ # ffmpeg used to report EXACTLY the same size, newer versions perhaps not
+ [ $subxsize -ne $subslen ] && logit "Warning: ffmpeg reported different subs size to genindex (VDR 1.x) for $1 ($subslen kb vs. $subxsize kb)"
+ map="-i \"$subsfile\" $F_SUBS $map"
+ echo;echo "Final subs map is $map"
+ echo
+ else
+ logit "Warning: only $subxsize kb subtitles in $1 (VDR 1.x), expected min. $minsubsize kb: none/broken/only in ads: none converted"
+ fi
+ else
+ logit "Skipping subtitles in $1 per command"
+ fi
+
+ # Main AV stream(s). Lose any subs here, as they won't (all) copy in ONE go, nice as that would be!
+ # Also required to ensure streams sync properly when combined.
+ # Need copyts on main streams to sync subs when present. Can cause ffmpeg to exit early if no subs or DTS issues
+ copyts=""
+ [ "$F_SUBS" ] && copyts="-copyts"
+ main="$tempdir/main.ts"
+ cmd="$ffmpeg $copyts -i "\"$tmpts"\" -c:v copy -c:a copy -sn -dn -map 0 "\"$main"\""
+ sh -c $cmd
+ if [ $? -ne 0 ]; then
+ logit "Fail: ffmpeg problem extracting main streams of $main (VDR 1.x), deleting (check $LOGFILE)"
+ [ $debug -eq 0 ] && rm -f $main || logit "could not remove $main"
+ return 1
+ fi
+ [ $debug -eq 0 ] && rm -f "$tmpts"
+ fi
+ #end VDR1.x
+ else
+ # VDR2
+ progduration $main
+ program_duration=$(($program_duration + $duration)) # combine all files.
+ fi
+
+ # The main conversion
+ map $main 0 # Additive map for VDR1.x conversions (add in subs file/map as reqd)
+ reportedvideo=$F_VIDEO
+ reportedaudio=$F_AUDIO
+
+ if [ "$F_AUDIO" ]; then # Only if we see something meaningful (may be only audio if radio recording)
+ if [ $keep -eq 1 ]; then
+ # might want to compress more for temp use, but never if keeping
+ [ $h264 -eq 1 -o $aac -eq 1 ] && logit "Skipping $main: already H264 or AAC" && return 2
+ # LATM multiplexing matches broadcast MPEG4 HD audio streams, but can't currently be demuxed to plain AAC by FFMPEG
+ # So even if keeping (e.g. automatic VDR transcode), without video use plain AAC so can transcode again if req'd
+ [ "$F_VIDEO" ] && [ $mpeg -eq 0 ] && latm="-mpegts_flags latm"
+ fi
+ #copyts is already in the map if/when req'd
+ cmd="$ffmpeg $debug_time -i "\"$main"\" $map $F_VIDEO $F_AUDIO $F_SUBS -f mpegts $latm "\"$2"\" 2>> $LOGFILE"
+ echo
+ logit "Executing $cmd"
+ sh -c $cmd
+ if [ $? -ne 0 ]; then
+ logit "Fail: ffmpeg problem converting $input, deleting "\"$2"\" !"
+ rm -f "$2" || logit "could not remove "\"$2"\""
+ return 1
+ fi
+ # clean up
+ else
+ logit "Fail: Nothing to convert for $input !"
+ return 1
+ fi
+ if [ $VDRtype ]; then
+ [ $debug -eq 0 ] && rm -rf "$tempdir" || logit "could not remove $tempdir"
+ fi
+ map="" # clear in case we process more files
+ return 0
+}
+
+#------------------------------------------------------------------------------
+
+function upload() {
+ # Upload to an ftp server for faster remote download.
+ # Note that ncftp is not always reliable, seems to randomly quit.
+ # Ideally need to a size compare & retry function
+ # NCFTP debug logs don't really help
+ logit "Uploading $1"
+ sh -c "ncftpput -z -t 99 -u \"$user\" -p \"$pass\" \"$server\" \"$ftppath\" \"$1\" &"
+}
+
+function usage() {
+ echo "usage: $0 --input-path | -i \"<recording.rec>\" [--keep |-k] [--combine |-c] [--delete |-d] [--extras |-e \"<ffmpeg opts>\"] [--nosubs] |-n] [--life |-l <days>] [--mpeg |-m] |--quality |-q <CRF>] [--yadif |-y <0|1>] [--tsonly |-t] [--crop-top <lines>] [--ftp] [--ftp-path <target path>] [--redo] |-r ] [--server |-s] [--user |-u] [--pass |-p]"
+}
+
+function escape() {
+ sed 's/'"'"'/'"\'"'/g'
+}
+
+function cleanup() {
+ # logit "Deleting intermediate file(s)"
+ cmd="rm -f $TMPFILES"
+ [ $debug -eq 0 ] && sh -c $cmd || logit "could not remove file(s)"
+}
+
+#------------------------------------------------------------------------------
+# Start of script
+#------------------------------------------------------------------------------
+
+# Default parameters. Most can be overridden by command line options
+
+ftp=0 # FTP out the transcoded file
+redo=0 # resend named file by FTP
+keep=0 # save as named file or replace recording with compressed version
+subs=1 # Subs cause us 95% of problems. Option to drop them. See ffmpeg bug #5617
+life=8 # If recording lifetime >= this, we compress if keeping, otherwise skip to save time & energy
+ts=0
+delete=0 # safety - by default we don't delete originals, even if nominally "keeping" them
+dif="" # deinterlacer mode: 0=send_frame (25 fps), 1=send_field (50 fps).
+ # 15-25% file size increase but also a bit better on old/noisy analogue material
+debug=0 # In debug mode, only short conversions are done, and intermediate files kept
+debug_time=""
+combine=0 # concatenate files before conversion. Limit is a total 16GB (in Genindex)
+duration=0
+
+# Default conversion parameters
+# Tuned to give balance of reasonable size and quality - for single use
+stereoquality=4
+stereobitrate=128
+#aac, 5.1 etc, BBC AAC output reported as 200
+#Note due to issues around nonfree AAC encoders and need to specially compile
+#ffmpeg, now just copy the audio stream if multi-channel
+multibitrate=192
+#likely AD stream. CBR is best for this
+monobitrate=50
+#Video - see https://trac.handbrake.fr/wiki/ConstantQuality
+#default, use higher for kept files. (20-21 good as long as you use medium or slower x264 preset)
+quality=23
+top=0 # optional cropping
+mpeg=0
+preset="faster"
+
+while [ "$1" != "" ]; do
+ case $1 in
+ -i | --input ) shift
+ input="$1"
+ ;;
+ --debug ) debug=1
+ debug_time="-t 00:01:00"
+ ;;
+ --crop-top ) shift
+ top="$1"
+ ;;
+ -c | --combine ) combine=1
+ ;;
+ -d | --delete ) delete=1
+ ;;
+ -e | --extras ) shift
+ extras="$1"
+ ;;
+ -k | --keep ) keep=1
+ quality=20.5
+ stereoquality=3
+ stereobitrate=160
+ preset="medium" # better actual quality at same CRF. Can go slower but unconvinced of benefit
+ ;;
+ -l | --life ) shift
+ life=$(echo $1|sed -e 's/[^0-9]//g')
+ ;;
+ -m | --mpeg2 ) mpeg=1
+ ;;
+ -n | --nosubs ) subs=0
+ ;;
+ -q | --quality ) shift
+ quality=$(echo $1|sed -e 's/[^0-9]//g')
+ ;;
+ -y | --dif ) shift
+ dif=$(echo $1|sed -e 's/[^0-9]//g')
+ ;;
+ -t | --tsonly ) ts=1
+ ;;
+ --ftp ) ftp=1
+ ;;
+ -r | --redo ) redo=1
+ ;;
+ --ftp-path ) shift
+ ftppath="$1"
+ ;;
+ -s | --server ) shift
+ server="$1"
+ ;;
+ -u | --user ) shift
+ user="$1"
+ ;;
+ -p | --pass ) shift
+ pass="$1"
+ ;;
+ -h | --help ) usage
+ exit 0
+ ;;
+ * ) usage
+ exit 1
+ esac
+ shift
+done
+
+[ ! -d "$input" ] && logit "Recording directory '"$input"' doesn't exist!" && usage && exit 0
+[ $dif ] && [ $dif -gt 1 ] && logit "deinterlacer config must be 0 or 1" && exit 1
+
+# VDR has been known to "start/stop" multiple times at the end of a recording (VPS?) - so prevent multiple instances
+LOCKFILE="$input/vdr-convert.pid"
+
+# check for existing lockfile
+if [ -e "$LOCKFILE" ]; then
+ # lockfile exists
+ [ -r "$LOCKFILE" ] || { logit "Error: cannot read lockfile"; exit 1; }
+ PID=$(cat "$LOCKFILE")
+ kill -0 "$(cat "$LOCKFILE")" 2>/dev/null && { logit "Error: existing instance of $0 is already running, exiting"; exit 1; }
+ # process that created lockfile is no longer running - delete lockfile
+ rm -f "$LOCKFILE" || { logit "Error: failed to delete $LOCKFILE"; exit 1; }
+fi
+
+# create lockfile
+echo $$ >"$LOCKFILE" || { logit "Error: cannot create lockfile for $0"; exit 1; }
+
+logit "Processing recording "\"$input"\", keep=$keep, delete=$delete, combine=$combine, subs=$subs, extras=$extras"
+
+# VDR filetypes 1.x (PES) or 2.x (TS), based on filenames in recording directory
+
+#vdrfiles=(`find "$1" -type f -name '*.vdr' | wc -l`)
+#tsfiles=(`find "$1" -type f -name '*.ts' | wc -l`)
+vdrfiles=0; tsfiles=0
+vdrfiles=($(ls -1 "$input" | grep ".vdr$" |grep -o '[0-9]\{3\}'| wc -l))
+tsfiles=($(ls -1 "$input" | grep ".ts$" |grep -o '[0-9]\{5\}'| wc -l))
+# must have 1 data file min, ideally an info file. We don't care about index file here
+[ $vdrfiles -lt 1 -a $tsfiles -lt 1 ] && logit "vdr=$vdrfiles,ts=$tsfiles : too few recording / info files!" && quit 1
+
+# Assume there are multiple files, as merging is v. quick
+if [ $vdrfiles -gt 0 ]; then
+ VDRtype=".vdr"
+ FILES="$input/0*.vdr"
+else
+ VDRtype=""
+ FILES="$input/0*.ts"
+fi
+#count=(`echo $FILES | wc -w`)
+
+[ $VDRtype ] && [ $ts -eq 1 ] && logit "Skipping $input, t flag and not a .ts" && quit 0
+
+# Get all relevant metadata out of the info(.vdr) file, if it exists
+[ -e "$input/info$VDRtype" ] && meta=$(cat "$input/info$VDRtype")
+
+TITLE="NO TITLE"
+[ "$meta" ] && for line in "$meta";
+do
+ #naming
+ TITLE=$(echo "$line" |grep "^T" |sed -e "s/^T //")
+ # optional fields - add them for improved ID
+ ID[0]=$(echo "$line" |grep "^S" |sed -e "s/^S //") #subtitle
+ EP=$(echo "$line" |grep "^D" |grep -oP '(?<=Episode:).*' |cut -f1 -d"|")
+ # Meta info event date, time and DOW, 3rd field - useful in file listings for offline use
+ ID[2]=$(echo "$line" |grep "^E" |cut -f3 -d" " |awk '{print strftime("%F-%a_%H-%M",$1)}')
+
+ #resolution. Just look for HD in station name - a bit simplistic, but only used in naming
+ HD=$(echo "$line" |grep "^C" | cut -f3-7 -d" "| grep -o 'HD')
+
+ #Stream metatdata also. Video we don't check - no lang usually associated
+ X201=$(echo "$line" |grep "^X 2 01\|^X 2 02" |cut -f4 -d" ")
+ X203=$(echo "$line" |grep "^X 2 03" |cut -f4 -d" ")
+ X204=$(echo "$line" |grep "^X 2 04" |cut -f4 -d" ")
+ X205=$(echo "$line" |grep "^X 2 05" |cut -f4 -d" ")
+ X240=$(echo "$line"|grep "^X 2 40" |cut -f4 -d" ")
+ X31=$(echo "$line" |grep "^X 3 1" |cut -f4 -d" ")
+ [ !$VDRtype ] && lifetime=$(echo "$line" |grep "^L" |sed 's/[^0-9]*//g')
+done
+
+# There are often multiples, take the first - naff, I know
+IFS=' ' read -ra X201 <<<"$X201"
+[ $X201 ] && Alangmono=$X201
+IFS=' ' read -ra X203 <<<"$X203"
+[ $X203 ] && Alangstereo=$X203
+IFS=' ' read -ra X204 <<<"$X204"
+[ $X204 ] && Alangmulti=$X204
+IFS=' ' read -ra X205 <<<"$X205"
+[ $X205 ] && Alangsurround=$X205
+IFS=' ' read -ra X240 <<<"$X240"
+[ $X240 ] && ADlang=$X240
+IFS=' ' read -ra X31 <<<"$X31"
+[ $X31 ] && Slang=$X31
+
+[ "$EP" ] && ID[1]=EPISODE$EP
+
+for token in ${ID[@]}
+do
+ [ "$token" ] && TITLE=$(echo "$TITLE-$token")
+done
+
+if [ $VDRtype ]; then
+ ROOTDIR=$(echo $(dirname "$input"))
+ BASEDIR=$(echo $(basename "$input"))
+ priority=$(echo $BASEDIR | cut -f4 -d".")
+ lifetime=$(echo $BASEDIR | cut -f5 -d".")
+fi
+
+# Short lifetime files skipped if we are "keeping" them - save time & energy
+[ $lifetime -lt $life ] && [ $keep -eq 1 ] && logit "Skipping $input, recording lifetime is $lifetime" && quit 0
+
+# Option to combine input files: Useful if there are lots of very small fragments due to interruptions.
+# Usually we convert separately, in case a broken file fails. Combining might be less reliable,
+# esp. with lots of interruptions, ffmpeg duration estimations can often get messed up
+if [ $combine -eq 1 ] && [ $vdrfiles -gt 1 -o $tsfiles -gt 1 ] && [ $redo -ne 1 ]; then
+ cat $FILES > "$input/recording.orig"
+ if [ $? -eq 0 ]; then
+ FILES="$input/recording.orig"
+ else
+ logit "Failed to combine input files for $input, trying individually" && rm -f "$input/recording.orig"
+ fi
+fi
+
+# Conversion
+for f in $FILES
+do
+ size=$(stat -c %s "$f" )
+ if [ $size -gt $minsize ]; then
+ totalsize=$(($totalsize + $size))
+ fileno=$(($fileno + 1))
+ echo
+ logit "Processing $f file $fileno, size $(($size/$meg))M..."
+ TMPFILE=$(echo "$TITLE-file-$fileno" | tr -s "\/:?*<>| " '-')
+ OUTFILE="$TMPFILE.$ext"
+ if [ $keep -eq 1 ]; then
+ OUTFILE="$input/$OUTFILE"
+ else
+ OUTFILE="$(pwd)/$OUTFILE" # default to current
+ fi
+ TMPFILES=$(echo "$TMPFILES \"$OUTFILE"\")
+ # horrible but mkvmerge doesn't like +file +file
+ if [ $fileno -eq 1 ]; then
+ MERGEFILES=$OUTFILE
+ else
+ MERGEFILES=$(echo "$MERGEFILES|$OUTFILE")
+# HandbrakeCLI
+# MERGEFILES=`echo "$MERGEFILES +$OUTFILE"`
+# # ffmpeg with mkv - not so good
+# MERGEFILES=`echo "$MERGEFILES|$OUTFILE"`
+ fi
+ if [ $redo -eq 0 ]; then
+ convertvdr "$f" "$OUTFILE"
+ return=$?
+ if [ $debug -eq 0 ] && [ $combine -eq 1 ] && [ -e "$input/recording.orig" ]; then
+ rm -f "$input/recording.orig" || logit "could not remove $input/recording.orig"
+ fi
+ [ $return -eq 1 ] && quit 1
+ [ $return -eq 2 ] && quit 0
+ fi
+ else
+ logit "Skipping $f - too small ($(($size/$meg))M). Try combining input files with -c option"
+ # !!!continue but no longer delete (so don't lose anything if it goes wrong)
+ [ $keep -eq 1 ] && [ $delete -eq 1 ] && delete=0 && logit "No longer deleting files for $input"
+ fi
+done
+
+
+# Map the last output file
+# Why? Sometimes a stream actually has no content (e.g. AD or subs),
+# even if the stream notionally existed when probed, ffmpeg usually drops it silently if empty,
+# resulting in a different map in the output file
+# ffmpeg still needs a specific map to copy more than just "the best" single audio & video stream.
+# For VDR1.x conversions, this also fixes the output stream mapping order (audio and subs first -> subs last)
+# Note map of each file SHOULD be the same, or there will be trouble.
+
+map $OUTFILE 0
+
+# SD or HD?. Enhanced EPG would tell us - but not always present
+if [ $F_VIDEO ]; then
+ DEF=$HD
+ [ ! $DEF ] && DEF="SD"
+else
+ [ $F_AUDIO ] && DEF="AUDIO"
+fi
+
+# Now merge them, avoid faffing about with separate files
+NAME="$TITLE-$DEF"
+BASENAME=$(echo "$TITLE" | tr -s "\/:?*<>| " '-')
+if [ $keep -eq 1 ]; then
+ OUTFILE="$input/$BASENAME.$ext"
+else
+ # On a "redo" can't test for type, so only use basename when ftp'ing
+ [ $ftp -eq 0 -a $redo -eq 0 ] && BASENAME="$BASENAME-$DEF"
+ OUTFILE="$(pwd)/$BASENAME.$ext" # default to current directory
+ AUDIO_ONLY="$(pwd)/$BASENAME.m4a"
+fi
+
+if [[ $redo -eq 0 && $MERGEFILES ]]; then
+ #ts's can be concatenated directly. Again probe deeply else can fail, esp on AD/subs.
+ # NOTE concat file better than concat stream: lipsync issues
+ # -copyts??? Without it ffmpeg reports duration correctly. Possibly not with copyts
+ cmd="$ffmpeg -i concat:"\"$MERGEFILES"\" -metadata title="\"$NAME"\" -c copy -flags +global_header $map "\"$OUTFILE"\" 2>> $LOGFILE"
+ echo;echo
+ logit "Executing $cmd"
+ sh -c $cmd
+ if [ $? -eq 0 ]; then
+ size=$(stat -c %s "$OUTFILE" )
+ if [ $keep -eq 1 ]; then
+ # check for duration match within 20s (empirical magic number)
+ progduration $OUTFILE
+ if [ $(($duration + 20)) -lt $program_duration ]; then
+ logit "Possible fail: $OUTFILE, size $(($size/$meg))M, is only $duration sec long, original $(($totalsize/$meg))M, with length detected as $program_duration sec"
+ cleanup
+ quit 1
+ fi
+ # check for silly compression ratio (converted less than 3/8 of original, or larger, heaven forbid!)
+ if [ $(($totalsize * 3)) -gt $(($size * 8)) -o $size -ge $totalsize -a $mpeg -eq 0 ]; then
+ logit "Possible fail: Suspect size of $OUTFILE, (new $(($size/$meg))M vs. original $(($totalsize/$meg))M)"
+ # keep converted outfile, actually it may be OK
+ cleanup
+ quit 1
+ fi
+ # Save video, force a VDR reindex and check it was really successful before deleting original files
+ # VDR2 will only recognise .ts's in the NEW format of directory name
+ if [ $VDRtype ]; then
+ # Need a new recording DIR formatted for VDR2/ts files, else VDR2 won't see it
+ # TV channel starts at 1. Channel number unavailable in info.vdr (& can change), just set to 1
+ # Resume ID 0 as resume different in VDR1.x
+ RECDIR=$(echo "$BASEDIR" | cut -f1-3 -d".")
+ RECDIR="$ROOTDIR/$RECDIR.1-0.rec"
+ mkdir -p -- "$RECDIR"
+ if [ $? -ne 0 ]; then
+ logit "Failed - cannot create $RECDIR"
+ cleanup
+ quit 1
+ fi
+ # marks don't need to change - they are time-related not stream related
+ [ -f "$input/marks.vdr" ] && cp -f "$input/marks.vdr" "$RECDIR/marks"
+ # Add the priority and Lifetime tags to new info
+ cp -f "$input/info.vdr" "$RECDIR/info"
+ echo "P $priority" >> "$RECDIR/info"
+ echo "L $lifetime" >> "$RECDIR/info"
+ # no resume as format changed
+ else
+ # VDR2 - Save ts's and index
+ RECDIR="$input"
+ SAVE="$input/0*.ts"
+ for f in $SAVE
+ do
+ mv -f "$f" "$f.orig" || logit "could not save original file(s)"
+ done
+ mv -f "$input/index" "$input/index.orig" || logit "could not save index"
+ fi
+ # Only a single ts after transcoding
+ mv -f "$OUTFILE" "$RECDIR/00001.ts" || logit "could not move .ts"
+ # Neatness, but importantly a check that VDR will be reasonably happy to play the file - otherwise bail
+ sh -c "vdr --genindex "\"$RECDIR"\""
+ if [ $? -eq 0 ]; then
+ stats="$(($totalsize/$meg))M to $(($size/$meg))M ($(echo $size $totalsize | awk '{ printf "%.1f", 100 * $1 / $2 }')%)"
+ echo "# Transcoded from $stats by $0 on $(date)" >> "$RECDIR/info"
+ [ "$F_VIDEO" ] && echo "# Video parameters: $reportedvideo" >> "$RECDIR/info"
+ [ "$F_AUDIO" ] && echo "# Audio parameters: $reportedaudio" >> "$RECDIR/info"
+ logit "Created/replaced .ts and indexed $NAME ($stats)"
+ if [ $delete -eq 1 ]; then
+ if [ $VDRtype ]; then
+ # old VDR 1.x directory & content no use now
+ [ $debug -eq 0 ] && rm -rf "$input" || logit "could not remove original file(s)"
+ else
+ [ $debug -eq 0 ] && rm -f "$input"/*.orig || logit "could not remove original file(s)"
+ fi
+ logit "Original files for $NAME deleted"
+ fi
+ else
+ # Failed, so restore
+ logit "Conversion / indexing for $NAME failed"
+ if [ $VDRtype ]; then
+ [ $debug -eq 0 ] && rm -rf "$RECDIR" || logit "could not delete temp conversion files" # borked new directory
+ else
+ # Overwrite
+ mv -f "$RECDIR/index.orig" "$RECDIR/index" || logit "could not restore index"
+ for f in $SAVE
+ do
+ mv -f "$f.orig" "$f" || logit "could not restore original video file"
+ done
+ fi
+ fi
+ # For audio-only recordings for use outside VDR, more portable to quickly remux to something more useful than .ts
+ elif [[ $DEF = "AUDIO" ]]; then
+ cmd="$ffmpeg -i "\"$OUTFILE"\" -c:a copy -bsf:a aac_adtstoasc "\"$AUDIO_ONLY"\" 2>> $LOGFILE"
+ sh -c $cmd
+ if [ $? -eq 0 ]; then
+ rm -f "$OUTFILE"
+ OUTFILE="$AUDIO_ONLY"
+ else
+ logit "Failed audio conversion to $AUDIO_ONLY" # leave it to check later
+ fi
+ fi
+ else
+ logit "Fail: Converted files for $NAME did not merge correctly"
+ fi
+ cleanup
+fi
+
+[ $ftp -eq 1 -o $redo -eq 1 -a $debug -eq 0 ] && upload "$OUTFILE"
+
+# remove lockfile
+quit 0
+
+# --------- $Id: vdr-convert,v 1.3 2016/09/01 13:01:48 richard Exp $ ---------- END