summaryrefslogtreecommitdiff
path: root/streamdev-server/externremux.sh
blob: 422e213245c4545ffca5b28dc0868b3b56dba742 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#!/bin/bash
#
# externremux.sh - sample remux script using mencoder for remuxing.
#
# Install this script as VDRCONFDIR/plugins/streamdev-server/externremux.sh
#
# The parameter QUALITY selects the default remux parameters. Adjust
# to your needs and point your web browser to http://servername:3000/ext/
# To select different remux parameters on the fly, insert a semicolon
# followed by the name and value of the requested parameter, e.g:
#   e.g. http://servername:3000/ext;QUALITY=WLAN11;VBR=512/
# The following parameters are recognized:
#
# PROG   actual remux program
# VC     video codec
# VBR    video bitrate (kbit)
# VOPTS  custom video options
# WIDTH  scale video to width
# HEIGHT scale video to height
# FPS    output frames per second
# AC     audio codec
# ABR    audio bitrate (kbit)
# AOPTS  custom audio options
#

##########################################################################

### GENERAL CONFIG START
###
# Pick one of DSL1000/DSL2000/DSL3000/DSL6000/DSL16000/LAN10/WLAN11/WLAN54
QUALITY='DSL1000'
# Program used for logging (logging disabled if empty)
LOGGER=logger
# Path and name of FIFO
FIFO=/tmp/externremux-${RANDOM:-$$}
# Default remux program (cat/mencoder/ogg)
PROG=mencoder
# Use mono if $ABR is lower than this value
ABR_MONO=64
###
### GENERAL CONFIG END

### MENCODER CONFIG START
###
# mencoder binary
MENCODER=mencoder
# verbosity from all=-1 to all=9 (-msglevel ...)
MENCODER_MSGLEVEL=all=1
### video part
# Default video codec (e.g. lavc/x264/copy)
MENCODER_VC=lavc
# Default video options if lavc is used (-ovc lavc -lavcopts ...)
MENCODER_LAVC_VOPTS=vcodec=mpeg4
# Default video options if x264 is used (-ovc x264 -x264encopts ...)
MENCODER_X264_VOPTS=threads=auto
### audio part
# Audio language to use if several audio PIDs are available (-alang ...)
MENCODER_ALANG=eng
# Default audio codec (e.g. lavc/mp3lame/faac/copy)
MENCODER_AC=mp3lame
# Default audio options if lavc is used (-oac lavc -lavcopts ...)
MENCODER_LAVC_AOPTS=acodec=mp2
# Default audio options if mp3lame is used (-oac mp3lame -lameopts ...)
MENCODER_LAME_AOPTS=
# Default audio options if faac is used (-oac faac -faacopts ...)
MENCODER_FAAC_AOPTS=
###
### MENCODER CONFIG END

### OGG CONFIG START
###
# ffmpeg2theora binary 
OGG=ffmpeg2theora
# speedlevel - lower value gives better quality but is slower (0..2)
OGG_SPEED=1
# videoquality - higher value gives better quality but is slower (0..10)
OGG_VQUALITY=0
# audioquality - higher value gives better quality but is slower (0..10)
OGG_AQUALITY=0
# aspect ratio used for scaling if only one of HEIGHT/WIDTH given (16/9 or 4/3)
OGG_ASPECT='4 / 3'
###
### OGG CONFIG END

##########################################################################

function hasOpt { echo "$1" | grep -q "\b${2}\b"; }

# $1:    concatenation of already set option=value pairs
# $2-$n: option=value pairs to be echod if the option is not present in $1
function addOpts
{
	local opts="$1"
	shift
	while [ $# -gt 0 ]; do
		hasOpt "$opts" ${1%%=*}= || echo $1
		shift
	done
}

function isNumeric() { echo "$@" | grep -q '^-\?[0-9]\{1,\}$'; }

function remux_cat
{
	startReply
	exec 3<&0
	cat 0<&3 >"$FIFO" &
}

function remux_mencoder
{
	# lavc may be used for video and audio
	LAVCOPTS=()

	# Assemble video options
	VC=${REMUX_PARAM_VC:-$MENCODER_VC}
	VOPTS=${REMUX_PARAM_VOPTS}
	FPS=${REMUX_PARAM_FPS:-$FPS}

	# if only one of HEIGHT/WIDTH given:
	# have mencoder calculate other value depending on actual aspect ratio
	if [ "$HEIGHT" -a -z "$WIDTH" ]; then
	  WIDTH=-3
	elif [ "$WIDTH" -a -z "$HEIGHT" ]; then
	  HEIGHT=-3
	fi

	case "$VC" in
		lavc)
			LAVCOPTS=(
				${VOPTS}
				$(IFS=$IFS:; addOpts "$VOPTS" $MENCODER_LAVC_VOPTS)
				${VBR:+vbitrate=$VBR}
			)
			[ ${#LAVCOPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -lavcopts "${LAVCOPTS[*]}")
			;; 
		x264)
			isNumeric "$HEIGHT" && [ $HEIGHT -lt 0 -a $HEIGHT -gt -8 ] && ((HEIGHT-=8))
			isNumeric "$WIDTH" && [ $WIDTH -lt 0 -a $WIDTH -gt -8 ] && ((WIDTH-=8))
			X264OPTS=(
				${VOPTS}
				$(IFS=$IFS:; addOpts "$VOPTS" $MENCODER_X264_VOPTS)
				${VBR:+bitrate=$VBR}
			)
			[ ${#X264OPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -x264encopts "${X264OPTS[*]}")
			;;
		copy)
			VOPTS=
			;;
		*)
			error "Unknown video codec '$VC'"
			;;
	esac

	# Assemble audio options 
	AC=${REMUX_PARAM_AC:-$MENCODER_AC}
	AOPTS=${REMUX_PARAM_AOPTS}
	case "$AC" in
		lavc)
			LAVCOPTS=(
				${LAVCOPTS[*]}
				${AOPTS}
				$(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_LAVC_AOPTS)
				${ABR:+abitrate=$ABR}
			)
	
			[ ${#LAVCOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -lavcopts "${LAVCOPTS[*]}")
			# lavc used for video and audio decoding - wipe out VOPTS as video options became part of AOPTS
			[ "$VC" = lavc ] && VOPTS=
			;; 
		mp3lame)
			LAMEOPTS=(
				${AOPTS}
				$(isNumeric "${ABR}" && [ "${ABR}" -lt "$ABR_MONO" ] && ! hasOpt "${AOPTS}" mode ] && echo 'mode=3')
				$(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_LAME_AOPTS)
				${ABR:+preset=$ABR}
			)
			[ ${#LAMEOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -lameopts "${LAMEOPTS[*]}")
			;;
		faac)
			FAACOPTS=(
				${AOPTS}
				$(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_FAAC_AOPTS)
				${ABR:+br=$ABR}
			)
			[ ${#FAACOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -faacopts "${FAACOPTS[*]}")
			;;
		copy)
			AOPTS=
			;;
		*)
			error "Unknown audio codec '$AC'"
			;;
	esac


	startReply
	exec 3<&0
	echo $MENCODER \
		${MENCODER_MSGLEVEL:+-msglevel $MENCODER_MSGLEVEL} \
		-ovc $VC $VOPTS \
		-oac $AC $AOPTS \
		${MENCODER_ALANG:+-alang $MENCODER_ALANG} \
		${WIDTH:+-vf scale=$WIDTH:$HEIGHT -zoom} \
		${FPS:+-ofps $FPS} \
		-o "$FIFO" -- - >&2
	$MENCODER \
		${MENCODER_MSGLEVEL:+-msglevel $MENCODER_MSGLEVEL} \
		-ovc $VC $VOPTS \
		-oac $AC $AOPTS \
		${MENCODER_ALANG:+-alang $MENCODER_ALANG} \
		${WIDTH:+-vf scale=$WIDTH:$HEIGHT -zoom} \
		${FPS:+-ofps $FPS} \
		-o "$FIFO" -- - 0<&3 >/dev/null &
}

function remux_ogg
{
	VOPTS=${REMUX_PARAM_VOPTS//[:=]/ }
	AOPTS=${REMUX_PARAM_AOPTS//[:=]/ }

	# if only one of HEIGHT/WIDTH given:
	# calculate other value depending on configured aspect ratio
	# trim to multiple of 8
	if [ "$HEIGHT" -a -z "$WIDTH" ]; then
	  WIDTH=$((HEIGHT * $OGG_ASPECT / 8 * 8))
	elif [ "$WIDTH" -a -z "$HEIGHT" ]; then
	  HEIGHT=$(($WIDTH * $( echo $OGG_ASPECT | sed 's#^\([0-9]\+\) */ *\([0-9]\+\)$#\2 / \1#') / 8 * 8))
	fi

	OGGOPTS=(
		${VOPTS}
		${VBR:+--videobitrate $VBR}
		$(hasOpt "${VOPTS}" videoquality || echo "--videoquality $OGG_VQUALITY")
		$(hasOpt "${VOPTS}" speedlevel || echo "--speedlevel $OGG_SPEED")
		${AOPTS}
		${ABR:+--audiobitrate $ABR}
		$(isNumeric "${ABR}" && [ "${ABR}" -lt "$ABR_MONO" ] && ! hasOpt "${AOPTS}" channels ] && echo '--channels 1')
		$(hasOpt "${AOPTS}" audioquality || echo "--audioquality $OGG_AQUALITY")
		$(hasOpt "${AOPTS}" audiostream || echo '--audiostream 1')
	)

	startReply
	exec 3<&0
	echo $OGG --format ts \
		${OGGOPTS[*]} \
		${WIDTH:+--width $WIDTH --height $HEIGHT} \
                --title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \
                --output "$FIFO" -- - 0<&3 >&2
	$OGG --format ts \
		${OGGOPTS[*]} \
		${WIDTH:+--width $WIDTH --height $HEIGHT} \
                --title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \
                --output "$FIFO" -- - 0<&3 >/dev/null &
}

function error
{
	if [ "$SERVER_PROTOCOL" = HTTP ]; then
		echo -ne "Content-type: text/plain\r\n"
		echo -ne '\r\n'
		echo "$*"
	fi

	echo "$*" >&2
	exit 1
}

function startReply
{
	if [ "$SERVER_PROTOCOL" = HTTP ]; then
		# send content-type and custom headers
		echo -ne "Content-type: ${CONTENTTYPE}\r\n"
		for header in "${HEADER[@]}"; do echo -ne "$header\r\n"; done
		echo -ne '\r\n'

		# abort after headers
		[ "$REQUEST_METHOD" = HEAD ] && exit 0
	fi

	# create FIFO and read from it in the background
	mkfifo "$FIFO"
	trap "trap '' EXIT HUP INT TERM ABRT PIPE CHLD; kill -INT 0; sleep 1; fuser -k '$FIFO'; rm '$FIFO'" EXIT HUP INT TERM ABRT PIPE CHLD
	cat "$FIFO" <&- &
}

HEADER=()

[ "$LOGGER" ] && exec 2> >($LOGGER -t "vdr: [$$] ${0##*/}" 2>&-)

# set default content-types
case "$REMUX_VPID" in
	''|0|1) CONTENTTYPE='audio/mpeg';;
	*)      CONTENTTYPE='video/mpeg';;
esac

QUALITY=${REMUX_PARAM_QUALITY:-$QUALITY}
case "$QUALITY" in
	DSL1000|dsl1000)   VBR=96;   ABR=16;  WIDTH=160;;
	DSL2000|dsl2000)   VBR=128;  ABR=16;  WIDTH=160;;
	DSL3000|dsl3000)   VBR=256;  ABR=16;  WIDTH=320;;
	DSL6000|dsl6000)   VBR=378;  ABR=32;  WIDTH=320;;
	DSL16000|dsl16000) VBR=512;  ABR=32;  WIDTH=480;;
	WLAN11|wlan11)     VBR=768;  ABR=64;  WIDTH=640;;
	WLAN54|wlan54)     VBR=2048; ABR=128; WIDTH=;;
	LAN10|lan10)       VBR=4096; ABR=;    WIDTH=;;
	*)                 error "Unknown quality '$QUALITY'";;
esac
ABR=${REMUX_PARAM_ABR:-$ABR}
VBR=${REMUX_PARAM_VBR:-$VBR}
WIDTH=${REMUX_PARAM_WIDTH:-$WIDTH}
HEIGHT=${REMUX_PARAM_HEIGHT:-$HEIGHT}
PROG=${REMUX_PARAM_PROG:-$PROG}

case "$PROG" in
	cat)      remux_cat;;
	mencoder) remux_mencoder;;
	ogg)      remux_ogg;;
	*)        error "Unknown remuxer '$PROG'";;
esac

set -o monitor
wait