mirror of
https://github.com/datarhei/restreamer.git
synced 2025-12-11 22:14:02 +01:00
Allow encoding the pulled stream to h264
This commit is contained in:
30
Dockerfile
30
Dockerfile
@@ -7,6 +7,7 @@ MAINTAINER datarhei <info@datarhei.org>
|
|||||||
ARG NASM_VERSION=2.14.02
|
ARG NASM_VERSION=2.14.02
|
||||||
ARG LAME_VERSION=3.100
|
ARG LAME_VERSION=3.100
|
||||||
ARG X264_VERSION=20190409-2245-stable
|
ARG X264_VERSION=20190409-2245-stable
|
||||||
|
ARG X265_VERSION=3.0
|
||||||
ARG FFMPEG_VERSION=4.1.3
|
ARG FFMPEG_VERSION=4.1.3
|
||||||
ARG NGINX_VERSION=1.14.2
|
ARG NGINX_VERSION=1.14.2
|
||||||
ARG NGINXRTMP_VERSION=1.2.1
|
ARG NGINXRTMP_VERSION=1.2.1
|
||||||
@@ -25,7 +26,8 @@ RUN apt-get update && \
|
|||||||
libssl-dev \
|
libssl-dev \
|
||||||
zlib1g-dev \
|
zlib1g-dev \
|
||||||
libasound2-dev \
|
libasound2-dev \
|
||||||
build-essential
|
build-essential \
|
||||||
|
cmake
|
||||||
|
|
||||||
# nasm
|
# nasm
|
||||||
RUN mkdir -p /dist && cd /dist && \
|
RUN mkdir -p /dist && cd /dist && \
|
||||||
@@ -45,6 +47,15 @@ RUN mkdir -p /dist && cd /dist && \
|
|||||||
make -j$(nproc) && \
|
make -j$(nproc) && \
|
||||||
make install
|
make install
|
||||||
|
|
||||||
|
# x265
|
||||||
|
RUN mkdir -p /dist && cd /dist && \
|
||||||
|
curl -OL "http://ftp.videolan.org/pub/videolan/x265/x265_${X265_VERSION}.tar.gz" && \
|
||||||
|
tar -xvz -f x265_${X265_VERSION}.tar.gz && \
|
||||||
|
cd x265_${X265_VERSION}/build && \
|
||||||
|
cmake ../source && \
|
||||||
|
make -j$(nproc) && \
|
||||||
|
make install
|
||||||
|
|
||||||
# libmp3lame
|
# libmp3lame
|
||||||
RUN mkdir -p /dist && cd /dist && \
|
RUN mkdir -p /dist && cd /dist && \
|
||||||
curl -OL "https://kent.dl.sourceforge.net/project/lame/lame/${LAME_VERSION}/lame-${LAME_VERSION}.tar.gz" && \
|
curl -OL "https://kent.dl.sourceforge.net/project/lame/lame/${LAME_VERSION}/lame-${LAME_VERSION}.tar.gz" && \
|
||||||
@@ -54,11 +65,14 @@ RUN mkdir -p /dist && cd /dist && \
|
|||||||
make -j$(nproc) && \
|
make -j$(nproc) && \
|
||||||
make install
|
make install
|
||||||
|
|
||||||
# ffmpeg
|
# ffmpeg && patch
|
||||||
|
COPY ./contrib/ffmpeg /dist/restreamer/contrib/ffmpeg
|
||||||
|
|
||||||
RUN mkdir -p /dist && cd /dist && \
|
RUN mkdir -p /dist && cd /dist && \
|
||||||
curl -OL "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz" && \
|
curl -OL "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz" && \
|
||||||
tar -xvz -f ffmpeg-${FFMPEG_VERSION}.tar.gz && \
|
tar -xvz -f ffmpeg-${FFMPEG_VERSION}.tar.gz && \
|
||||||
cd ffmpeg-${FFMPEG_VERSION} && \
|
cd ffmpeg-${FFMPEG_VERSION} && \
|
||||||
|
patch -p1 < /dist/restreamer/contrib/ffmpeg/bitrate.patch && \
|
||||||
./configure \
|
./configure \
|
||||||
--bindir="${SRC}/bin" \
|
--bindir="${SRC}/bin" \
|
||||||
--extra-cflags="-I${SRC}/include" \
|
--extra-cflags="-I${SRC}/include" \
|
||||||
@@ -69,6 +83,7 @@ RUN mkdir -p /dist && cd /dist && \
|
|||||||
--enable-version3 \
|
--enable-version3 \
|
||||||
--enable-libmp3lame \
|
--enable-libmp3lame \
|
||||||
--enable-libx264 \
|
--enable-libx264 \
|
||||||
|
--enable-libx265 \
|
||||||
--enable-openssl \
|
--enable-openssl \
|
||||||
--enable-postproc \
|
--enable-postproc \
|
||||||
--enable-small \
|
--enable-small \
|
||||||
@@ -98,17 +113,6 @@ RUN mkdir -p /dist && cd /dist && \
|
|||||||
cp -R bin /usr/local && \
|
cp -R bin /usr/local && \
|
||||||
cp -R lib /usr/local
|
cp -R lib /usr/local
|
||||||
|
|
||||||
RUN rm -r /dist && \
|
|
||||||
apt-get remove -y \
|
|
||||||
pkg-config \
|
|
||||||
curl \
|
|
||||||
libpcre3-dev \
|
|
||||||
libtool \
|
|
||||||
libssl-dev \
|
|
||||||
zlib1g-dev \
|
|
||||||
build-essential && \
|
|
||||||
apt autoremove -y
|
|
||||||
|
|
||||||
FROM $IMAGE
|
FROM $IMAGE
|
||||||
|
|
||||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
COPY --from=builder /usr/local/bin /usr/local/bin
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ MAINTAINER datarhei <info@datarhei.org>
|
|||||||
|
|
||||||
ARG LAME_VERSION=3.100
|
ARG LAME_VERSION=3.100
|
||||||
ARG X264_VERSION=20190409-2245-stable
|
ARG X264_VERSION=20190409-2245-stable
|
||||||
|
ARG X265_VERSION=3.0
|
||||||
ARG FFMPEG_VERSION=4.1.3
|
ARG FFMPEG_VERSION=4.1.3
|
||||||
ARG NGINX_VERSION=1.14.2
|
ARG NGINX_VERSION=1.14.2
|
||||||
ARG NGINXRTMP_VERSION=1.2.1
|
ARG NGINXRTMP_VERSION=1.2.1
|
||||||
@@ -44,11 +45,14 @@ RUN mkdir -p /dist && cd /dist && \
|
|||||||
make -j$(nproc) && \
|
make -j$(nproc) && \
|
||||||
make install
|
make install
|
||||||
|
|
||||||
# ffmpeg
|
# ffmpeg && patch
|
||||||
|
COPY ./contrib/ffmpeg /dist/restreamer/contrib/ffmpeg
|
||||||
|
|
||||||
RUN mkdir -p /dist && cd /dist && \
|
RUN mkdir -p /dist && cd /dist && \
|
||||||
curl -OL "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz" && \
|
curl -OL "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz" && \
|
||||||
tar -xvz -f ffmpeg-${FFMPEG_VERSION}.tar.gz && \
|
tar -xvz -f ffmpeg-${FFMPEG_VERSION}.tar.gz && \
|
||||||
cd ffmpeg-${FFMPEG_VERSION} && \
|
cd ffmpeg-${FFMPEG_VERSION} && \
|
||||||
|
patch -p1 < /dist/restreamer/contrib/ffmpeg/bitrate.patch && \
|
||||||
./configure \
|
./configure \
|
||||||
--bindir="${SRC}/bin" \
|
--bindir="${SRC}/bin" \
|
||||||
--extra-cflags="-I${SRC}/include" \
|
--extra-cflags="-I${SRC}/include" \
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ MAINTAINER datarhei <info@datarhei.org>
|
|||||||
|
|
||||||
ARG LAME_VERSION=3.100
|
ARG LAME_VERSION=3.100
|
||||||
ARG X264_VERSION=20190409-2245-stable
|
ARG X264_VERSION=20190409-2245-stable
|
||||||
|
ARG X265_VERSION=3.0
|
||||||
ARG FFMPEG_VERSION=4.1.3
|
ARG FFMPEG_VERSION=4.1.3
|
||||||
ARG NGINX_VERSION=1.14.2
|
ARG NGINX_VERSION=1.14.2
|
||||||
ARG NGINXRTMP_VERSION=1.2.1
|
ARG NGINXRTMP_VERSION=1.2.1
|
||||||
@@ -44,11 +45,14 @@ RUN mkdir -p /dist && cd /dist && \
|
|||||||
make -j$(nproc) && \
|
make -j$(nproc) && \
|
||||||
make install
|
make install
|
||||||
|
|
||||||
# ffmpeg
|
# ffmpeg && patch
|
||||||
|
COPY ./contrib/ffmpeg /dist/restreamer/contrib/ffmpeg
|
||||||
|
|
||||||
RUN mkdir -p /dist && cd /dist && \
|
RUN mkdir -p /dist && cd /dist && \
|
||||||
curl -OL "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz" && \
|
curl -OL "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz" && \
|
||||||
tar -xvz -f ffmpeg-${FFMPEG_VERSION}.tar.gz && \
|
tar -xvz -f ffmpeg-${FFMPEG_VERSION}.tar.gz && \
|
||||||
cd ffmpeg-${FFMPEG_VERSION} && \
|
cd ffmpeg-${FFMPEG_VERSION} && \
|
||||||
|
patch -p1 < /dist/restreamer/contrib/ffmpeg/bitrate.patch && \
|
||||||
./configure \
|
./configure \
|
||||||
--bindir="${SRC}/bin" \
|
--bindir="${SRC}/bin" \
|
||||||
--extra-cflags="-I${SRC}/include" \
|
--extra-cflags="-I${SRC}/include" \
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ MAINTAINER datarhei <info@datarhei.org>
|
|||||||
|
|
||||||
ARG LAME_VERSION=3.100
|
ARG LAME_VERSION=3.100
|
||||||
ARG X264_VERSION=20190409-2245-stable
|
ARG X264_VERSION=20190409-2245-stable
|
||||||
|
ARG X265_VERSION=3.0
|
||||||
ARG FFMPEG_VERSION=4.1.3
|
ARG FFMPEG_VERSION=4.1.3
|
||||||
ARG NGINX_VERSION=1.14.2
|
ARG NGINX_VERSION=1.14.2
|
||||||
ARG NGINXRTMP_VERSION=1.2.1
|
ARG NGINXRTMP_VERSION=1.2.1
|
||||||
@@ -44,11 +45,14 @@ RUN mkdir -p /dist && cd /dist && \
|
|||||||
make -j$(nproc) && \
|
make -j$(nproc) && \
|
||||||
make install
|
make install
|
||||||
|
|
||||||
# ffmpeg
|
# ffmpeg && patch
|
||||||
|
COPY ./contrib/ffmpeg /dist/restreamer/contrib/ffmpeg
|
||||||
|
|
||||||
RUN mkdir -p /dist && cd /dist && \
|
RUN mkdir -p /dist && cd /dist && \
|
||||||
curl -OL "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz" && \
|
curl -OL "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz" && \
|
||||||
tar -xvz -f ffmpeg-${FFMPEG_VERSION}.tar.gz && \
|
tar -xvz -f ffmpeg-${FFMPEG_VERSION}.tar.gz && \
|
||||||
cd ffmpeg-${FFMPEG_VERSION} && \
|
cd ffmpeg-${FFMPEG_VERSION} && \
|
||||||
|
patch -p1 < /dist/restreamer/contrib/ffmpeg/bitrate.patch && \
|
||||||
./configure \
|
./configure \
|
||||||
--bindir="${SRC}/bin" \
|
--bindir="${SRC}/bin" \
|
||||||
--extra-cflags="-I${SRC}/include" \
|
--extra-cflags="-I${SRC}/include" \
|
||||||
|
|||||||
@@ -1,81 +1,168 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"definitions": {},
|
||||||
"id": "http://jsonschema.net",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"type": "object",
|
"$id": "http://jsonschema.net/",
|
||||||
"properties": {
|
"type": "object",
|
||||||
"addresses": {
|
"required": [
|
||||||
"id": "http://jsonschema.net/addresses",
|
"addresses",
|
||||||
"type": "object",
|
"options",
|
||||||
"properties": {
|
"states",
|
||||||
"srcAddress": {
|
"userActions"
|
||||||
"id": "http://jsonschema.net/addresses/srcAddress",
|
],
|
||||||
"type": "string"
|
"properties": {
|
||||||
},
|
"addresses": {
|
||||||
"optionalOutputAddress": {
|
"type": "object",
|
||||||
"id": "http://jsonschema.net/addresses/optionalOutputAddress",
|
"required": [
|
||||||
"type": "string"
|
"srcAddress",
|
||||||
}
|
"optionalOutputAddress"
|
||||||
},
|
],
|
||||||
"required": [
|
"properties": {
|
||||||
"srcAddress",
|
"srcAddress": {
|
||||||
"optionalOutputAddress"
|
"type": "string",
|
||||||
]
|
"default": "",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
},
|
},
|
||||||
"options": {
|
"optionalOutputAddress": {
|
||||||
"id": "http://jsonschema.net/options",
|
"type": "string",
|
||||||
"type": "object",
|
"default": "",
|
||||||
"properties": {
|
"pattern": "^(.*)$"
|
||||||
"rtspTcp": {
|
|
||||||
"id": "http://jsonschema.net/options/rtspTcp",
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"states": {
|
|
||||||
"id": "http://jsonschema.net/states",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"repeatToLocalNginx": {
|
|
||||||
"id": "http://jsonschema.net/states/repeatToLocalNginx",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"type": {
|
|
||||||
"id": "http://jsonschema.net/states/repeatToLocalNginx/type",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"repeatToOptionalOutput": {
|
|
||||||
"id": "http://jsonschema.net/states/repeatToOptionalOutput",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"type": {
|
|
||||||
"id": "http://jsonschema.net/states/repeatToOptionalOutput/type",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"userActions": {
|
|
||||||
"id": "http://jsonschema.net/userActions",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"repeatToLocalNginx": {
|
|
||||||
"id": "http://jsonschema.net/userActions/repeatToLocalNginx",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"repeatToOptionalOutput": {
|
|
||||||
"id": "http://jsonschema.net/userActions/repeatToOptionalOutput",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"options": {
|
||||||
"addresses",
|
"type": "object",
|
||||||
"options",
|
"required": [
|
||||||
"states",
|
"rtspTcp"
|
||||||
"userActions"
|
],
|
||||||
]
|
"properties": {
|
||||||
|
"rtspTcp": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"video": {
|
||||||
|
"type": "object",
|
||||||
|
"default": null,
|
||||||
|
"required": [
|
||||||
|
"codec",
|
||||||
|
"preset",
|
||||||
|
"bitrate",
|
||||||
|
"fps"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"codec": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "copy",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
},
|
||||||
|
"preset": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "ultrafast",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
},
|
||||||
|
"bitrate": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "4096",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
},
|
||||||
|
"fps": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "25",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"audio": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"codec",
|
||||||
|
"preset",
|
||||||
|
"bitrate",
|
||||||
|
"channels",
|
||||||
|
"sampling"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"codec": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "copy",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
},
|
||||||
|
"preset": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "silence",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
},
|
||||||
|
"bitrate": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "64",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
},
|
||||||
|
"channels": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "mono",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
},
|
||||||
|
"sampling": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "44100",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"states": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"repeatToLocalNginx",
|
||||||
|
"repeatToOptionalOutput"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"repeatToLocalNginx": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repeatToOptionalOutput": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"userActions": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"repeatToLocalNginx",
|
||||||
|
"repeatToOptionalOutput"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"repeatToLocalNginx": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
},
|
||||||
|
"repeatToOptionalOutput": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"pattern": "^(.*)$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
122
conf/live.json
122
conf/live.json
@@ -8,72 +8,90 @@
|
|||||||
},
|
},
|
||||||
"ffmpeg": {
|
"ffmpeg": {
|
||||||
"options": {
|
"options": {
|
||||||
"native_h264_native_audio": {
|
"audio_codec_copy": {
|
||||||
"outputOptions": [
|
"outputOptions": [
|
||||||
"-codec copy",
|
"-codec:a copy"
|
||||||
"-map 0:v",
|
|
||||||
"-map 0:a"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"native_h264_no_audio": {
|
"audio_codec_copy_aac": {
|
||||||
"outputOptions": [
|
"outputOptions": [
|
||||||
"-vcodec copy",
|
"-codec:a copy",
|
||||||
"-an",
|
|
||||||
"-map 0:v"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"native_h264_native_aac": {
|
|
||||||
"outputOptions": [
|
|
||||||
"-codec copy",
|
|
||||||
"-map 0:v",
|
|
||||||
"-map 0:a",
|
|
||||||
"-bsf:a aac_adtstoasc"
|
"-bsf:a aac_adtstoasc"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"native_h264_silence_aac": {
|
"audio_codec_none": {
|
||||||
"input": "anullsrc=r=44100:cl=mono",
|
"outputOptions": [
|
||||||
|
"-an"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"audio_codec_aac": {
|
||||||
|
"outputOptions": [
|
||||||
|
"-codec:a aac",
|
||||||
|
"-bsf:a aac_adtstoasc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"audio_codec_mp3": {
|
||||||
|
"outputOptions": [
|
||||||
|
"-codec:a libmp3lame"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"audio_preset_copy": {
|
||||||
|
"outputOptions": [
|
||||||
|
"-map 0:a"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"audio_preset_encode": {
|
||||||
|
"outputOptions": [
|
||||||
|
"-b:a {bitrate}k",
|
||||||
|
"-map 0:a"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"audio_preset_silence": {
|
||||||
|
"input": "anullsrc=r={sampling}:cl={channels}",
|
||||||
"inputOptions": [
|
"inputOptions": [
|
||||||
"-f lavfi"
|
"-f lavfi",
|
||||||
|
"-thread_queue_size 512"
|
||||||
],
|
],
|
||||||
"outputOptions": [
|
"outputOptions": [
|
||||||
"-vcodec copy",
|
"-b:a {bitrate}k",
|
||||||
"-acodec aac",
|
|
||||||
"-b:a 0k",
|
|
||||||
"-map 0:v",
|
|
||||||
"-map 1:a",
|
"-map 1:a",
|
||||||
"-shortest"
|
"-shortest"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"native_h264_transcode_aac": {
|
"video_codec_copy": {
|
||||||
"outputOptions": [
|
"outputOptions": [
|
||||||
"-vcodec copy",
|
"-codec:v copy",
|
||||||
"-acodec aac",
|
|
||||||
"-b:a 64k",
|
|
||||||
"-map 0:v",
|
"-map 0:v",
|
||||||
"-map 0:a"
|
"-vsync 0",
|
||||||
|
"-copyts",
|
||||||
|
"-start_at_zero"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"native_h264_silence_mp3": {
|
"video_codec_h264": {
|
||||||
"input": "anullsrc=r=44100:cl=mono",
|
|
||||||
"inputOptions": [
|
|
||||||
"-f lavfi"
|
|
||||||
],
|
|
||||||
"outputOptions": [
|
"outputOptions": [
|
||||||
"-vcodec copy",
|
"-codec:v libx264",
|
||||||
"-acodec libmp3lame",
|
"-preset:v {preset}",
|
||||||
"-b:a 0k",
|
"-b:v {bitrate}k",
|
||||||
"-map 0:v",
|
"-maxrate {bitrate}k",
|
||||||
"-map 1:a",
|
"-bufsize {bitrate}k",
|
||||||
"-shortest"
|
"-r {fps}",
|
||||||
|
"-g {gop}",
|
||||||
|
"-map 0:v"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"native_h264_transcode_mp3": {
|
"video_codec_hevc": {
|
||||||
"outputOptions": [
|
"outputOptions": [
|
||||||
"-vcodec copy",
|
"-codec:v libx265",
|
||||||
"-acodec libmp3lame",
|
"-preset:v {preset}",
|
||||||
"-b:a 64k",
|
"-b:v {bitrate}k",
|
||||||
|
"-maxrate {bitrate}k",
|
||||||
|
"-bufsize {bitrate}k",
|
||||||
|
"-r {fps}",
|
||||||
|
"-x265-params keyint={gop}",
|
||||||
"-map 0:v",
|
"-map 0:v",
|
||||||
"-map 0:a"
|
"-hls_segment_type fmp4",
|
||||||
|
"-hls_fmp4_init_filename live.stream_init.mp4",
|
||||||
|
"-hls_segment_filename /tmp/hls/live.stream_%09d.m4s"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"global": {
|
"global": {
|
||||||
@@ -86,11 +104,25 @@
|
|||||||
"video": {
|
"video": {
|
||||||
"outputOptions": [
|
"outputOptions": [
|
||||||
"-map_metadata -1",
|
"-map_metadata -1",
|
||||||
"-metadata application=datarhei/Restreamer",
|
"-metadata application=datarhei/Restreamer"
|
||||||
"-metadata server=NGINX-RTMP",
|
]
|
||||||
|
},
|
||||||
|
"rtmp": {
|
||||||
|
"outputOptions": [
|
||||||
"-f flv"
|
"-f flv"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"hls": {
|
||||||
|
"inputOptions": [
|
||||||
|
"-fflags nobuffer+genpts"
|
||||||
|
],
|
||||||
|
"outputOptions": [
|
||||||
|
"-f hls",
|
||||||
|
"-hls_time 2",
|
||||||
|
"-hls_list_size 10",
|
||||||
|
"-hls_flags delete_segments+temp_file+append_list"
|
||||||
|
]
|
||||||
|
},
|
||||||
"rtsp-tcp": {
|
"rtsp-tcp": {
|
||||||
"inputOptions": [
|
"inputOptions": [
|
||||||
"-rtsp_transport tcp"
|
"-rtsp_transport tcp"
|
||||||
|
|||||||
38
contrib/ffmpeg/bitrate.patch
Normal file
38
contrib/ffmpeg/bitrate.patch
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
|
||||||
|
index f502961..d2b4bf9 100644
|
||||||
|
--- a/fftools/ffmpeg.c
|
||||||
|
+++ b/fftools/ffmpeg.c
|
||||||
|
@@ -1651,8 +1651,7 @@ static void print_report(int is_last_report, int64_t timer_start, int64_t cur_ti
|
||||||
|
{
|
||||||
|
AVBPrint buf, buf_script;
|
||||||
|
OutputStream *ost;
|
||||||
|
- AVFormatContext *oc;
|
||||||
|
- int64_t total_size;
|
||||||
|
+ int64_t total_size = 0;
|
||||||
|
AVCodecContext *enc;
|
||||||
|
int frame_number, vid, i;
|
||||||
|
double bitrate;
|
||||||
|
@@ -1680,13 +1679,6 @@ static void print_report(int is_last_report, int64_t timer_start, int64_t cur_ti
|
||||||
|
|
||||||
|
t = (cur_time-timer_start) / 1000000.0;
|
||||||
|
|
||||||
|
-
|
||||||
|
- oc = output_files[0]->ctx;
|
||||||
|
-
|
||||||
|
- total_size = avio_size(oc->pb);
|
||||||
|
- if (total_size <= 0) // FIXME improve avio_size() so it works with non seekable output too
|
||||||
|
- total_size = avio_tell(oc->pb);
|
||||||
|
-
|
||||||
|
vid = 0;
|
||||||
|
av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
|
||||||
|
av_bprint_init(&buf_script, 0, AV_BPRINT_SIZE_AUTOMATIC);
|
||||||
|
@@ -1761,6 +1753,9 @@ static void print_report(int is_last_report, int64_t timer_start, int64_t cur_ti
|
||||||
|
ost->st->time_base, AV_TIME_BASE_Q));
|
||||||
|
if (is_last_report)
|
||||||
|
nb_frames_drop += ost->last_dropped;
|
||||||
|
+
|
||||||
|
+ total_size += ost->data_size;
|
||||||
|
+ total_size += ost->enc_ctx->extradata_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
secs = FFABS(pts) / AV_TIME_BASE;
|
||||||
@@ -31,13 +31,13 @@ class Restreamer {
|
|||||||
let nginx = config.nginx.streaming;
|
let nginx = config.nginx.streaming;
|
||||||
let token = process.env.RS_TOKEN || config.auth.token;
|
let token = process.env.RS_TOKEN || config.auth.token;
|
||||||
|
|
||||||
let hlspath = 'rtmp://' + nginx.ip + ':' + nginx.rtmp_port + nginx.rtmp_hls_path + 'live.stream';
|
let path = 'rtmp://' + nginx.ip + ':' + nginx.rtmp_port + nginx.rtmp_hls_path + 'live.stream';
|
||||||
|
|
||||||
if(token != '') {
|
if(token != '') {
|
||||||
hlspath += '?token=' + token;
|
path += '?token=' + token;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hlspath;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,8 +79,8 @@ class Restreamer {
|
|||||||
|
|
||||||
command.output(Restreamer.getSnapshotPath());
|
command.output(Restreamer.getSnapshotPath());
|
||||||
|
|
||||||
Restreamer.addStreamOptions(command, 'global');
|
Restreamer.addStreamOptions(command, 'global', null);
|
||||||
Restreamer.addStreamOptions(command, 'snapshot');
|
Restreamer.addStreamOptions(command, 'snapshot', null);
|
||||||
|
|
||||||
command.on('start', (commandLine) => {
|
command.on('start', (commandLine) => {
|
||||||
logger.debug('Spawned: ' + commandLine, 'snapshot');
|
logger.debug('Spawned: ' + commandLine, 'snapshot');
|
||||||
@@ -99,7 +99,7 @@ class Restreamer {
|
|||||||
command.exec();
|
command.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
static addStreamOptions(command, name) {
|
static addStreamOptions(command, name, replace) {
|
||||||
if(!(name in config.ffmpeg.options)) {
|
if(!(name in config.ffmpeg.options)) {
|
||||||
logger.debug('Unknown option: ' + name);
|
logger.debug('Unknown option: ' + name);
|
||||||
return;
|
return;
|
||||||
@@ -109,16 +109,49 @@ class Restreamer {
|
|||||||
|
|
||||||
let options = config.ffmpeg.options[name];
|
let options = config.ffmpeg.options[name];
|
||||||
|
|
||||||
|
let replacer = function(options, replace) {
|
||||||
|
if(replace == null) {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
let replaced_options = [];
|
||||||
|
|
||||||
|
if(typeof options != 'string') {
|
||||||
|
for(let i = 0; i < options.length; i++) {
|
||||||
|
let option = options[i];
|
||||||
|
for(let r in replace) {
|
||||||
|
if(option.indexOf('{'+r+'}') != -1) {
|
||||||
|
logger.debug('Replacing {'+r+'} with "'+replace[r]+'" in: '+option);
|
||||||
|
option = option.replace('{'+r+'}', replace[r]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
replaced_options.push(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let option = options;
|
||||||
|
for(let r in replace) {
|
||||||
|
if(options.indexOf('{'+r+'}') != -1) {
|
||||||
|
logger.debug('Replacing {'+r+'} with "'+replace[r]+'" in: '+option);
|
||||||
|
option = option.replace('{'+r+'}', replace[r]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
replaced_options = option;
|
||||||
|
}
|
||||||
|
|
||||||
|
return replaced_options;
|
||||||
|
}
|
||||||
|
|
||||||
if('input' in options) {
|
if('input' in options) {
|
||||||
command.input(options.input);
|
command.input(replacer(options.input, replace));
|
||||||
}
|
}
|
||||||
|
|
||||||
if('inputOptions' in options) {
|
if('inputOptions' in options) {
|
||||||
command.inputOptions(options.inputOptions);
|
command.inputOptions(replacer(options.inputOptions, replace));
|
||||||
}
|
}
|
||||||
|
|
||||||
if('outputOptions' in options) {
|
if('outputOptions' in options) {
|
||||||
command.outputOptions(options.outputOptions);
|
command.outputOptions(replacer(options.outputOptions, replace));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,71 +320,81 @@ class Restreamer {
|
|||||||
return deferred.reject("no video stream detected");
|
return deferred.reject("no video stream detected");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(video.codec_name != 'h264') {
|
let options = {
|
||||||
return deferred.reject("video stream must be h264, found " + video.codec_name);
|
audio: [],
|
||||||
}
|
video: []
|
||||||
|
};
|
||||||
let option = "native_h264_no_audio";
|
|
||||||
|
|
||||||
if(streamType == 'repeatToLocalNginx') {
|
if(streamType == 'repeatToLocalNginx') {
|
||||||
|
if(video.codec_name != 'h264' && Restreamer.data.options.video.codec == 'copy') {
|
||||||
|
return deferred.reject("video stream must be h264, found " + video.codec_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Restreamer.data.options.video.codec == 'h264') {
|
||||||
|
options.video.push('video_codec_h264');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(video.codec_name != 'h264') {
|
||||||
|
return deferred.reject("video stream must be h264, found " + video.codec_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.video.push('video_codec_copy');
|
||||||
|
}
|
||||||
|
|
||||||
if(audio !== null) {
|
if(audio !== null) {
|
||||||
switch(audio.codec_name) { // consider all allowed audio codecs for FLV
|
if(Restreamer.data.options.audio.codec == 'none') {
|
||||||
case 'mp3':
|
options.audio.push('audio_codec_none');
|
||||||
case 'pcm_alaw':
|
}
|
||||||
case 'pcm_mulaw':
|
else if(Restreamer.data.options.audio.codec == 'aac') {
|
||||||
option = "native_h264_native_audio"; break;
|
options.audio.push('audio_codec_aac');
|
||||||
case 'aac':
|
options.audio.push('audio_preset_'+Restreamer.data.options.audio.preset);
|
||||||
option = "native_h264_native_aac"; break;
|
}
|
||||||
default:
|
else if(Restreamer.data.options.audio.codec == 'mp3') {
|
||||||
option = "native_h264_transcode_aac"; break;
|
options.audio.push('audio_codec_mp3');
|
||||||
|
options.audio.push('audio_preset_'+Restreamer.data.options.audio.preset);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch(audio.codec_name) { // consider all allowed audio codecs for FLV
|
||||||
|
case 'mp3':
|
||||||
|
case 'pcm_alaw':
|
||||||
|
case 'pcm_mulaw':
|
||||||
|
options.audio.push('audio_codec_copy');
|
||||||
|
break;
|
||||||
|
case 'aac':
|
||||||
|
options.audio.push('audio_codec_copy_aac');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return deferred.reject("can't copy audio stream, found unsupported codec " + audio.codec_name);
|
||||||
|
}
|
||||||
|
options.audio.push('audio_preset_copy');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
option = "native_h264_silence_aac";
|
if(Restreamer.data.options.audio.codec == 'aac') {
|
||||||
}
|
options.audio.push('audio_codec_aac');
|
||||||
|
options.audio.push('audio_preset_silence');
|
||||||
if(process.env.RS_AUDIO == "none") {
|
}
|
||||||
option = "native_h264_no_audio";
|
else if(Restreamer.data.options.audio.codec == 'mp3') {
|
||||||
}
|
options.audio.push('audio_codec_mp3');
|
||||||
else if(process.env.RS_AUDIO == "silence") {
|
options.audio.push('audio_preset_silence');
|
||||||
option = "native_h264_silence_aac";
|
|
||||||
}
|
|
||||||
else if(process.env.RS_AUDIO == "aac") {
|
|
||||||
if(audio !== null) {
|
|
||||||
if(audio.codec_name != 'aac') {
|
|
||||||
option = "native_h264_transcode_aac";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
option = "native_h264_native_aac";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
option = "native_h264_silence_aac";
|
options.audio.push('audio_codec_none');
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(process.env.RS_AUDIO == "mp3") {
|
|
||||||
if(audio !== null) {
|
|
||||||
if(audio.codec_name != 'mp3') {
|
|
||||||
option = "native_h264_transcode_mp3";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
option = "native_h264_native_audio";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
option = "native_h264_silence_mp3";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
options.video.push('video_codec_copy');
|
||||||
|
|
||||||
if(audio !== null) {
|
if(audio !== null) {
|
||||||
option = "native_h264_native_audio";
|
options.audio.push('audio_codec_copy');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.audio.push('audio_codec_none');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Selected ffmpeg option: ' + option, streamType);
|
return deferred.resolve(options);
|
||||||
|
|
||||||
return deferred.resolve(option);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
@@ -475,7 +518,7 @@ class Restreamer {
|
|||||||
logger.debug('Skipping "startStream" because state is "connected" or "connecting". Current state is "' + state + '".', streamType);
|
logger.debug('Skipping "startStream" because state is "connected" or "connecting". Current state is "' + state + '".', streamType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the user has clicked 'stop' meanwhile, so the startStream process has to be skipped
|
// check if the user has clicked 'stop' meanwhile, so the startStream process has to be skipped
|
||||||
if(Restreamer.getUserAction(streamType) == 'stop') {
|
if(Restreamer.getUserAction(streamType) == 'stop') {
|
||||||
@@ -499,14 +542,13 @@ class Restreamer {
|
|||||||
stdoutLines: 1
|
stdoutLines: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
Restreamer.addStreamOptions(command, 'global');
|
Restreamer.addStreamOptions(command, 'global', null);
|
||||||
Restreamer.addStreamOptions(command, 'video');
|
Restreamer.addStreamOptions(command, 'video', null);
|
||||||
|
Restreamer.addStreamOptions(command, 'rtmp', null);
|
||||||
|
|
||||||
// GUI option
|
// GUI option
|
||||||
if(streamType == 'repeatToLocalNginx') {
|
if(Restreamer.data.options.rtspTcp && streamUrl.indexOf('rtsp') == 0) {
|
||||||
if(Restreamer.data.options.rtspTcp && Restreamer.data.addresses.srcAddress.indexOf('rtsp') == 0) {
|
Restreamer.addStreamOptions(command, 'rtsp-tcp', null);
|
||||||
Restreamer.addStreamOptions(command, 'rtsp-tcp');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add outputs to the ffmpeg stream
|
// add outputs to the ffmpeg stream
|
||||||
@@ -518,8 +560,9 @@ class Restreamer {
|
|||||||
stdoutLines: 1
|
stdoutLines: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
Restreamer.addStreamOptions(command, 'global');
|
Restreamer.addStreamOptions(command, 'global', null);
|
||||||
Restreamer.addStreamOptions(command, 'video');
|
Restreamer.addStreamOptions(command, 'video', null);
|
||||||
|
Restreamer.addStreamOptions(command, 'rtmp', null);
|
||||||
|
|
||||||
// add outputs to the ffmpeg stream
|
// add outputs to the ffmpeg stream
|
||||||
command.output(streamUrl);
|
command.output(streamUrl);
|
||||||
@@ -550,8 +593,27 @@ class Restreamer {
|
|||||||
let nFrames = -1;
|
let nFrames = -1;
|
||||||
|
|
||||||
// after adding outputs, define events on the new FFmpeg stream
|
// after adding outputs, define events on the new FFmpeg stream
|
||||||
probePromise.then((option) => {
|
probePromise.then((options) => {
|
||||||
Restreamer.addStreamOptions(command, option);
|
let replace_video = {
|
||||||
|
preset: Restreamer.data.options.video.preset,
|
||||||
|
bitrate: Restreamer.data.options.video.bitrate,
|
||||||
|
fps: Restreamer.data.options.video.fps,
|
||||||
|
gop: (parseInt(Restreamer.data.options.video.fps) * 2) + ''
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let o in options.video) {
|
||||||
|
Restreamer.addStreamOptions(command, options.video[o], replace_video);
|
||||||
|
}
|
||||||
|
|
||||||
|
let replace_audio = {
|
||||||
|
bitrate: Restreamer.data.options.audio.bitrate,
|
||||||
|
channels: Restreamer.data.options.audio.channels,
|
||||||
|
sampling: Restreamer.data.options.audio.sampling
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let o in options.audio) {
|
||||||
|
Restreamer.addStreamOptions(command, options.audio[o], replace_audio);
|
||||||
|
}
|
||||||
|
|
||||||
command
|
command
|
||||||
.on('start', (commandLine) => {
|
.on('start', (commandLine) => {
|
||||||
@@ -631,7 +693,7 @@ class Restreamer {
|
|||||||
|
|
||||||
command.exec();
|
command.exec();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
logger.debug('Failed to probe stream: ' + error.toString(), streamType);
|
logger.debug('Failed to spawn ffmpeg: ' + error.toString(), streamType);
|
||||||
|
|
||||||
if(Restreamer.data.userActions[streamType] == 'stop') {
|
if(Restreamer.data.userActions[streamType] == 'stop') {
|
||||||
Restreamer.updateState(streamType, 'disconnected');
|
Restreamer.updateState(streamType, 'disconnected');
|
||||||
@@ -794,7 +856,20 @@ Restreamer.data = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'options': {
|
'options': {
|
||||||
'rtspTcp': false
|
'rtspTcp': false,
|
||||||
|
'video': {
|
||||||
|
'codec': 'copy',
|
||||||
|
'preset': 'ultrafast',
|
||||||
|
'bitrate': '4096',
|
||||||
|
'fps': '25'
|
||||||
|
},
|
||||||
|
'audio': {
|
||||||
|
'codec': 'copy',
|
||||||
|
'preset': 'silence',
|
||||||
|
'bitrate': '64',
|
||||||
|
'channels': 'mono',
|
||||||
|
'sampling': '44100'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'states': {
|
'states': {
|
||||||
'repeatToLocalNginx': {
|
'repeatToLocalNginx': {
|
||||||
|
|||||||
@@ -46,6 +46,32 @@ class RestreamerData {
|
|||||||
throw new Error(JSON.stringify(validateResult.errors));
|
throw new Error(JSON.stringify(validateResult.errors));
|
||||||
} else {
|
} else {
|
||||||
logger.debug('"v1.db" is valid');
|
logger.debug('"v1.db" is valid');
|
||||||
|
|
||||||
|
// Fill up optional fields if not present
|
||||||
|
if(!('video' in dbdata.options)) {
|
||||||
|
dbdata.options.video = {
|
||||||
|
'codec': 'copy',
|
||||||
|
'preset': 'ultrafast',
|
||||||
|
'bitrate': '4096',
|
||||||
|
'fps': '25'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!('audio' in dbdata.options)) {
|
||||||
|
dbdata.options.audio = {
|
||||||
|
'codec': 'copy',
|
||||||
|
'preset': 'silence',
|
||||||
|
'bitrate': '64',
|
||||||
|
'channels': 'mono',
|
||||||
|
'sampling': '44100'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(dbPath)) {
|
||||||
|
fs.mkdirSync(dbPath);
|
||||||
|
}
|
||||||
|
fs.writeFileSync(path.join(dbPath, dbFile), JSON.stringify(dbdata));
|
||||||
|
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -56,7 +82,20 @@ class RestreamerData {
|
|||||||
'optionalOutputAddress': ''
|
'optionalOutputAddress': ''
|
||||||
},
|
},
|
||||||
'options': {
|
'options': {
|
||||||
'rtspTcp': true
|
'rtspTcp': true,
|
||||||
|
'video': {
|
||||||
|
'codec': 'copy',
|
||||||
|
'preset': 'ultrafast',
|
||||||
|
'bitrate': '4096',
|
||||||
|
'fps': '25'
|
||||||
|
},
|
||||||
|
'audio': {
|
||||||
|
'codec': 'copy',
|
||||||
|
'preset': 'silence',
|
||||||
|
'bitrate': '64',
|
||||||
|
'channels': 'mono',
|
||||||
|
'sampling': '44100'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'states': {
|
'states': {
|
||||||
'repeatToLocalNginx': {
|
'repeatToLocalNginx': {
|
||||||
|
|||||||
@@ -44,7 +44,19 @@ window.angular.module('Main').controller('mainController',
|
|||||||
|
|
||||||
$scope.reStreamerData = {
|
$scope.reStreamerData = {
|
||||||
'options': {
|
'options': {
|
||||||
'rtspTcp': false
|
'rtspTcp': false,
|
||||||
|
'video': {
|
||||||
|
'codec': 'copy',
|
||||||
|
'preset': 'ultrafast',
|
||||||
|
'bitrate': 4096
|
||||||
|
},
|
||||||
|
'audio': {
|
||||||
|
'codec': 'copy',
|
||||||
|
'preset': 'silence',
|
||||||
|
'bitrate': 64,
|
||||||
|
'channels': 'mono',
|
||||||
|
'sampling': 41000
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'states': {
|
'states': {
|
||||||
'repeatToLocalNginx': {
|
'repeatToLocalNginx': {
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ window.angular.module('StreamingInterface').controller('streamingStatusControlle
|
|||||||
* @returns {number} current bit rate
|
* @returns {number} current bit rate
|
||||||
*/
|
*/
|
||||||
$scope.kbps = () => {
|
$scope.kbps = () => {
|
||||||
return $scope.data.progresses ? $scope.data.progresses[$scope.name].currentKbps : 0;
|
var bitrate = $scope.data.progresses ? $scope.data.progresses[$scope.name].currentKbps : 0;
|
||||||
|
return bitrate.toFixed(1);
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|||||||
@@ -17,6 +17,116 @@
|
|||||||
<streaming-status name="repeatToLocalNginx" data="reStreamerData"></streaming-status>
|
<streaming-status name="repeatToLocalNginx" data="reStreamerData"></streaming-status>
|
||||||
|
|
||||||
<div class="form-inline form-group">
|
<div class="form-inline form-group">
|
||||||
|
<div style="margin-bottom:1em" ng-if="showStartButton('repeatToLocalNginx')">
|
||||||
|
<h4>Stream options:</h4>
|
||||||
|
<table style="width:100%">
|
||||||
|
<thread>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th class="text-center" style="width:150px"><strong>Video</strong></th>
|
||||||
|
<th class="text-center" style="width:150px"><strong>Audio</strong></th>
|
||||||
|
</tr>
|
||||||
|
</thread>
|
||||||
|
<tbody>
|
||||||
|
<tr style="height:24px">
|
||||||
|
<td>Codec</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<select class="input" ng-model="reStreamerData.options.video.codec">
|
||||||
|
<option value="copy" selected>copy</option>
|
||||||
|
<option value="h264">H264 (x264)</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<select class="input" ng-model="reStreamerData.options.audio.codec">
|
||||||
|
<option value="copy" selected>copy</option>
|
||||||
|
<option value="none">none</option>
|
||||||
|
<option value="aac">AAC</option>
|
||||||
|
<option value="mp3">MP3</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr><tr style="height:24px" ng-if="reStreamerData.options.video.codec != 'copy' || (reStreamerData.options.audio.codec != 'copy' && reStreamerData.options.audio.codec != 'none')">
|
||||||
|
<td>Preset</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<select class="input" ng-if="reStreamerData.options.video.codec != 'copy'" ng-model="reStreamerData.options.video.preset">
|
||||||
|
<option value="ultrafast" selected>ultrafast</option>
|
||||||
|
<option value="superfast">superfast</option>
|
||||||
|
<option value="veryfast">veryfast</option>
|
||||||
|
<option value="faster">faster</option>
|
||||||
|
<option value="fast">fast</option>
|
||||||
|
<option value="medium">medium</option>
|
||||||
|
<option value="slow">slow</option>
|
||||||
|
<option value="slower">slower</option>
|
||||||
|
<option value="veryslow">veryslow</option>
|
||||||
|
<option value="placebo">placebo</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<select class="input" ng-if="reStreamerData.options.audio.codec != 'copy' && reStreamerData.options.audio.codec != 'none'" ng-model="reStreamerData.options.audio.preset">
|
||||||
|
<option value="encode" selected>encode</option>
|
||||||
|
<option value="silence">silence</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr><tr style="height:24px" ng-if="reStreamerData.options.video.codec != 'copy' || (reStreamerData.options.audio.codec != 'copy' && reStreamerData.options.audio.codec != 'none')">
|
||||||
|
<td>Bitrate</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<select class="input" ng-if="reStreamerData.options.video.codec != 'copy'" ng-model="reStreamerData.options.video.bitrate">
|
||||||
|
<option value="32768">32 Mbit/s</option>
|
||||||
|
<option value="24576">24 Mbit/s</option>
|
||||||
|
<option value="20480">20 Mbit/s</option>
|
||||||
|
<option value="16384">16 Mbit/s</option>
|
||||||
|
<option value="12288">12 Mbit/s</option>
|
||||||
|
<option value="8192">8 Mbit/s</option>
|
||||||
|
<option value="4096" selected>4 Mbit/s</option>
|
||||||
|
<option value="2048">2 Mbit/s</option>
|
||||||
|
<option value="1024">1 Mbit/s</option>
|
||||||
|
<option value="512">512 Kbit/s</option>
|
||||||
|
<option value="256">256 Kbit/s</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<select class="input" ng-if="reStreamerData.options.audio.codec != 'copy' && reStreamerData.options.audio.codec != 'none'" ng-model="reStreamerData.options.audio.bitrate">
|
||||||
|
<option value="256">256 Kbit/s</option>
|
||||||
|
<option value="128">128 Kbit/s</option>
|
||||||
|
<option value="64" selected>64 Kbit/s</option>
|
||||||
|
<option value="32">32 Kbit/s</option>
|
||||||
|
<option value="16">16 Kbit/s</option>
|
||||||
|
<option value="8">8 Kbit/s</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr><tr style="height:24px" ng-if="reStreamerData.options.video.codec != 'copy'">
|
||||||
|
<td>FPS</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<select class="input" ng-if="reStreamerData.options.video.codec != 'copy'" ng-model="reStreamerData.options.video.fps">
|
||||||
|
<option value="60">60</option>
|
||||||
|
<option value="30">30</option>
|
||||||
|
<option value="25" selected>25</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr><tr style="height:24px" ng-if="reStreamerData.options.audio.codec != 'copy' && reStreamerData.options.audio.codec != 'none'">
|
||||||
|
<td>Channels</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="text-right">
|
||||||
|
<select class="input" ng-if="reStreamerData.options.audio.codec != 'copy' && reStreamerData.options.audio.codec != 'none'" ng-model="reStreamerData.options.audio.channels">
|
||||||
|
<option value="mono" selected>mono</option>
|
||||||
|
<option value="stereo">stereo</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr><tr style="height:24px" ng-if="reStreamerData.options.audio.codec != 'copy' && reStreamerData.options.audio.codec != 'none'">
|
||||||
|
<td>Sampling</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="text-right">
|
||||||
|
<select class="input" ng-if="reStreamerData.options.audio.codec != 'copy' && reStreamerData.options.audio.codec != 'none'" ng-model="reStreamerData.options.audio.sampling">
|
||||||
|
<option value="44100" selected>44100 Hz</option>
|
||||||
|
<option value="22050">22050 Hz</option>
|
||||||
|
<option value="11025">11025 Hz</option>
|
||||||
|
<option value="8000">8000 Hz</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
<div ng-if="reStreamerData.addresses.srcAddress.indexOf('rtsp') === 0 && showStartButton('repeatToLocalNginx')"
|
<div ng-if="reStreamerData.addresses.srcAddress.indexOf('rtsp') === 0 && showStartButton('repeatToLocalNginx')"
|
||||||
class="checkbox pull-left">
|
class="checkbox pull-left">
|
||||||
<label>
|
<label>
|
||||||
@@ -44,6 +154,7 @@
|
|||||||
{{'button_stop' | translate}}
|
{{'button_stop' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- todo: this hr to css border-bottom or something-->
|
<!-- todo: this hr to css border-bottom or something-->
|
||||||
|
|||||||
Reference in New Issue
Block a user