Compare commits
23 Commits
Author | SHA1 | Date |
---|---|---|
htt | 9cf50af569 | |
htt | fb4aebf3ec | |
Sebastian | 5025773aa1 | |
Sebastian | 2d2558b647 | |
Sebastian | ba0f582314 | |
Sebastian | 0454acb61d | |
Sebastian | c97e242a10 | |
Sebastian | c9aad3a16a | |
Sebastian | c2ce62f72d | |
Sebastian | b71788cdea | |
Sebastian | d29c68dba6 | |
Sebastian | 7dfdf794b4 | |
Sebastian | 13bfcced45 | |
Sebastian | 3984ad2214 | |
Sebastian | 3b8e0056c0 | |
Sebastian | 7269c2a678 | |
Sebastian | 614f215186 | |
Sebastian | f4bdbeb0da | |
Sebastian | 8c88ef6779 | |
Sebastian | 5e8978f57b | |
Sebastian | 8b0b59e792 | |
Sebastian | d0f9eec322 | |
Sebastian | c89b3d530e |
|
@ -1,5 +1,6 @@
|
|||
Snowmix-0.4.2
|
||||
Snowmix-*
|
||||
images/logo.*
|
||||
python/virtenv
|
||||
videos
|
||||
*.pyc
|
||||
*.swp
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,4 @@
|
|||
*.pkg.tar.xz
|
||||
gst-plugins-base
|
||||
pkg
|
||||
src
|
|
@ -0,0 +1,76 @@
|
|||
# $Id$
|
||||
# Maintainer: Jan de Groot <jgc@archlinux.org>
|
||||
|
||||
pkgbase=('gstreamer0.10-base')
|
||||
pkgname=('gstreamer0.10-base' 'gstreamer0.10-base-plugins')
|
||||
pkgver=0.10.36
|
||||
pkgrel=3
|
||||
arch=('i686' 'x86_64')
|
||||
license=('LGPL')
|
||||
makedepends=('pkgconfig' 'gstreamer0.10>=0.10.36' 'orc' 'libxv' 'alsa-lib' 'cdparanoia' 'libvisual' 'libvorbis' 'libtheora' 'pango' 'gobject-introspection' 'git')
|
||||
options=(!emptydirs)
|
||||
url="http://gstreamer.freedesktop.org/"
|
||||
source=("git://anongit.freedesktop.org/gstreamer-sdk/gst-plugins-base#commit=48d5966f12d4e6b71c96db0600cf76ef0ef14b3a"
|
||||
fix-crash-0-byte-ogg.patch
|
||||
colorbalance-fix-abi.patch
|
||||
revert-decodebin-playbin-removal.patch
|
||||
videoscale-fix-negotiation.patch
|
||||
ayuv64-lanczos.patch
|
||||
gstaudio-symbols.patch
|
||||
chinese-cam.patch)
|
||||
sha256sums=('SKIP'
|
||||
'a6a01035ea9627737f9c17f72919857ed43ccc7c2cb08b645b43ed89f78d0f4f'
|
||||
'7442c5c68068428b8c7ac1d3825ce29f1bb152b75b77047b9e806c7d322b780c'
|
||||
'ba20659fafea73db016ddaecd128f12087e0957ce35cf2c3ce29f72c51551ef3'
|
||||
'ae27f7be58997217f67898b37b138a485c203389e56b65e6b31c23f769ef39ca'
|
||||
'3792dfe80c69f51c0db98533e8fb16707b5dd2ee6933ea6098583af873ceb44a'
|
||||
'56e7a988df39d2ec4befa265536ad8c30d3c8d18d136cebef64e8d6baac1abae'
|
||||
'4bb8ecc75598a09bdb9dcb99ecc7f90963735704c68978ac6850686005f1bbfc')
|
||||
prepare() {
|
||||
cd gst-plugins-base
|
||||
sed -i -e '/AC_PATH_XTRA/d' -e 's/AM_CONFIG_HEADER/AC_CONFIG_HEADERS/' configure.ac
|
||||
patch -Np1 -i ../fix-crash-0-byte-ogg.patch
|
||||
patch -Np1 -i ../colorbalance-fix-abi.patch
|
||||
patch -Np1 -i ../ayuv64-lanczos.patch
|
||||
patch -Np1 -i ../videoscale-fix-negotiation.patch
|
||||
patch -Np1 -i ../gstaudio-symbols.patch
|
||||
patch -Np1 -R -i ../revert-decodebin-playbin-removal.patch
|
||||
patch -Np1 -i ../chinese-cam.patch
|
||||
}
|
||||
|
||||
build() {
|
||||
cd gst-plugins-base
|
||||
NOCONFIGURE=1 ./autogen.sh
|
||||
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var \
|
||||
--disable-static --enable-experimental --disable-gnome_vfs --disable-gtk-doc \
|
||||
--with-package-name="GStreamer Base Plugins (Archlinux)" \
|
||||
--with-package-origin="http://www.archlinux.org/"
|
||||
make
|
||||
sed -e 's/^SUBDIRS_EXT =.*/SUBDIRS_EXT =/' -i Makefile
|
||||
}
|
||||
|
||||
check() {
|
||||
cd gst-plugins-base
|
||||
make check
|
||||
}
|
||||
|
||||
package_gstreamer0.10-base() {
|
||||
pkgdesc="GStreamer Multimedia Framework Base plugin libraries"
|
||||
depends=('gstreamer0.10>=0.10.36' 'orc' 'libxv')
|
||||
|
||||
cd gst-plugins-base
|
||||
make DESTDIR="${pkgdir}" install
|
||||
}
|
||||
|
||||
package_gstreamer0.10-base-plugins() {
|
||||
pkgdesc="GStreamer Multimedia Framework Base Plugins (gst-plugins-base)"
|
||||
depends=("gstreamer0.10-base=${pkgver}" 'alsa-lib' 'cdparanoia' 'libvisual' 'libvorbis' 'libtheora' 'pango')
|
||||
replaces=('gstreamer0.10-alsa' 'gstreamer0.10-theora' 'gstreamer0.10-libvisual' 'gstreamer0.10-pango' 'gstreamer0.10-cdparanoia' 'gstreamer0.10-vorbis' 'gstreamer0.10-ogg')
|
||||
conflicts=('gstreamer0.10-alsa' 'gstreamer0.10-theora' 'gstreamer0.10-libvisual' 'gstreamer0.10-pango' 'gstreamer0.10-cdparanoia' 'gstreamer0.10-vorbis' 'gstreamer0.10-ogg')
|
||||
groups=('gstreamer0.10-plugins')
|
||||
|
||||
cd gst-plugins-base
|
||||
make -C gst-libs DESTDIR="${pkgdir}" install
|
||||
make -C ext DESTDIR="${pkgdir}" install
|
||||
make -C gst-libs DESTDIR="${pkgdir}" uninstall
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
From e4f01106d08f3c2a65897bfe919bd65ce55545a8 Mon Sep 17 00:00:00 2001
|
||||
From: David Schleef <ds@schleef.org>
|
||||
Date: Wed, 25 Jan 2012 23:49:00 +0000
|
||||
Subject: videoscale: Add AYUV64 path to Lanczos
|
||||
|
||||
---
|
||||
diff --git a/gst/videoscale/gstvideoscale.c b/gst/videoscale/gstvideoscale.c
|
||||
index 7b44647..6df4ba6 100644
|
||||
--- a/gst/videoscale/gstvideoscale.c
|
||||
+++ b/gst/videoscale/gstvideoscale.c
|
||||
@@ -1199,6 +1199,11 @@ gst_video_scale_transform (GstBaseTransform * trans, GstBuffer * in,
|
||||
case GST_VIDEO_SCALE_4TAP:
|
||||
vs_image_scale_4tap_AYUV64 (&dest, &src, videoscale->tmp_buf);
|
||||
break;
|
||||
+ case GST_VIDEO_SCALE_LANCZOS:
|
||||
+ vs_image_scale_lanczos_AYUV64 (&dest, &src, videoscale->tmp_buf,
|
||||
+ videoscale->sharpness, videoscale->dither, videoscale->submethod,
|
||||
+ videoscale->envelope, videoscale->sharpen);
|
||||
+ break;
|
||||
default:
|
||||
goto unknown_mode;
|
||||
}
|
||||
diff --git a/gst/videoscale/vs_image.h b/gst/videoscale/vs_image.h
|
||||
index 2312acc..7b34991 100644
|
||||
--- a/gst/videoscale/vs_image.h
|
||||
+++ b/gst/videoscale/vs_image.h
|
||||
@@ -52,6 +52,9 @@ void vs_image_scale_linear_RGBA (const VSImage *dest, const VSImage *src,
|
||||
void vs_image_scale_lanczos_AYUV (const VSImage * dest, const VSImage * src,
|
||||
uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod,
|
||||
double a, double sharpen);
|
||||
+void vs_image_scale_lanczos_AYUV64 (const VSImage * dest, const VSImage * src,
|
||||
+ uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod,
|
||||
+ double a, double sharpen);
|
||||
|
||||
void vs_image_scale_nearest_RGB (const VSImage *dest, const VSImage *src,
|
||||
uint8_t *tmpbuf);
|
||||
diff --git a/gst/videoscale/vs_lanczos.c b/gst/videoscale/vs_lanczos.c
|
||||
index 1c87ba3..d141a01 100644
|
||||
--- a/gst/videoscale/vs_lanczos.c
|
||||
+++ b/gst/videoscale/vs_lanczos.c
|
||||
@@ -204,6 +204,9 @@ static void vs_image_scale_lanczos_AYUV_float (const VSImage * dest,
|
||||
static void vs_image_scale_lanczos_AYUV_double (const VSImage * dest,
|
||||
const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
|
||||
double a, double sharpen);
|
||||
+static void vs_image_scale_lanczos_AYUV64_double (const VSImage * dest,
|
||||
+ const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
|
||||
+ double a, double sharpen);
|
||||
|
||||
static double
|
||||
sinc (double x)
|
||||
@@ -590,6 +593,15 @@ vs_image_scale_lanczos_AYUV (const VSImage * dest, const VSImage * src,
|
||||
}
|
||||
}
|
||||
|
||||
+void
|
||||
+vs_image_scale_lanczos_AYUV64 (const VSImage * dest, const VSImage * src,
|
||||
+ uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod,
|
||||
+ double a, double sharpen)
|
||||
+{
|
||||
+ vs_image_scale_lanczos_AYUV64_double (dest, src, tmpbuf, sharpness, dither,
|
||||
+ a, sharpen);
|
||||
+}
|
||||
+
|
||||
|
||||
|
||||
#define RESAMPLE_HORIZ_FLOAT(function, dest_type, tap_type, src_type, _n_taps) \
|
||||
@@ -813,9 +825,9 @@ RESAMPLE_VERT_DITHER (resample_vert_dither_int16_generic, gint16, gint16,
|
||||
n_taps, shift)
|
||||
/* *INDENT-ON* */
|
||||
|
||||
-#define RESAMPLE_VERT_FLOAT(function, tap_type, src_type, _n_taps, _shift) \
|
||||
+#define RESAMPLE_VERT_FLOAT(function, dest_type, clamp, tap_type, src_type, _n_taps, _shift) \
|
||||
static void \
|
||||
-function (guint8 *dest, \
|
||||
+function (dest_type *dest, \
|
||||
const tap_type *taps, const src_type *src, int stride, int n_taps, \
|
||||
int shift, int n) \
|
||||
{ \
|
||||
@@ -828,13 +840,13 @@ function (guint8 *dest, \
|
||||
const src_type *line = PTR_OFFSET(src, stride * l); \
|
||||
sum_y += line[i] * taps[l]; \
|
||||
} \
|
||||
- dest[i] = CLAMP (floor(0.5 + sum_y), 0, 255); \
|
||||
+ dest[i] = CLAMP (floor(0.5 + sum_y), 0, clamp); \
|
||||
} \
|
||||
}
|
||||
|
||||
-#define RESAMPLE_VERT_FLOAT_DITHER(function, tap_type, src_type, _n_taps, _shift) \
|
||||
+#define RESAMPLE_VERT_FLOAT_DITHER(function, dest_type, clamp, tap_type, src_type, _n_taps, _shift) \
|
||||
static void \
|
||||
-function (guint8 *dest, \
|
||||
+function (dest_type *dest, \
|
||||
const tap_type *taps, const src_type *src, int stride, int n_taps, \
|
||||
int shift, int n) \
|
||||
{ \
|
||||
@@ -849,19 +861,24 @@ function (guint8 *dest, \
|
||||
sum_y += line[i] * taps[l]; \
|
||||
} \
|
||||
err_y += sum_y; \
|
||||
- dest[i] = CLAMP (floor (err_y), 0, 255); \
|
||||
+ dest[i] = CLAMP (floor (err_y), 0, clamp); \
|
||||
err_y -= floor (err_y); \
|
||||
} \
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
-RESAMPLE_VERT_FLOAT (resample_vert_double_generic, double, double, n_taps,
|
||||
+RESAMPLE_VERT_FLOAT (resample_vert_double_generic, guint8, 255, double, double, n_taps,
|
||||
shift)
|
||||
-RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_double_generic, double, double,
|
||||
+RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_double_generic, guint8, 255, double, double,
|
||||
n_taps, shift)
|
||||
|
||||
-RESAMPLE_VERT_FLOAT (resample_vert_float_generic, float, float, n_taps, shift)
|
||||
-RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_float_generic, float, float,
|
||||
+RESAMPLE_VERT_FLOAT (resample_vert_double_generic_u16, guint16, 65535, double, double, n_taps,
|
||||
+ shift)
|
||||
+RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_double_generic_u16, guint16, 65535, double, double,
|
||||
+ n_taps, shift)
|
||||
+
|
||||
+RESAMPLE_VERT_FLOAT (resample_vert_float_generic, guint8, 255, float, float, n_taps, shift)
|
||||
+RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_float_generic, guint8, 255, float, float,
|
||||
n_taps, shift)
|
||||
/* *INDENT-ON* */
|
||||
|
||||
@@ -1556,3 +1573,77 @@ vs_image_scale_lanczos_AYUV_float (const VSImage * dest, const VSImage * src,
|
||||
scale1d_cleanup (&scale->y_scale1d);
|
||||
g_free (scale->tmpdata);
|
||||
}
|
||||
+
|
||||
+static void
|
||||
+vs_scale_lanczos_AYUV64_double (Scale * scale)
|
||||
+{
|
||||
+ int j;
|
||||
+ int yi;
|
||||
+ int tmp_yi;
|
||||
+
|
||||
+ tmp_yi = 0;
|
||||
+
|
||||
+ for (j = 0; j < scale->dest->height; j++) {
|
||||
+ guint16 *destline;
|
||||
+ double *taps;
|
||||
+
|
||||
+ destline = (guint16 *) (scale->dest->pixels + scale->dest->stride * j);
|
||||
+
|
||||
+ yi = scale->y_scale1d.offsets[j];
|
||||
+
|
||||
+ while (tmp_yi < yi + scale->y_scale1d.n_taps) {
|
||||
+ scale->horiz_resample_func (TMP_LINE_DOUBLE_AYUV (tmp_yi),
|
||||
+ scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
|
||||
+ scale->x_scale1d.n_taps, 0, scale->dest->width);
|
||||
+ tmp_yi++;
|
||||
+ }
|
||||
+
|
||||
+ taps = (double *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
|
||||
+ if (scale->dither) {
|
||||
+ resample_vert_dither_double_generic_u16 (destline,
|
||||
+ taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]),
|
||||
+ sizeof (double) * 4 * scale->dest->width,
|
||||
+ scale->y_scale1d.n_taps, 0, scale->dest->width * 4);
|
||||
+ } else {
|
||||
+ resample_vert_double_generic_u16 (destline,
|
||||
+ taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]),
|
||||
+ sizeof (double) * 4 * scale->dest->width,
|
||||
+ scale->y_scale1d.n_taps, 0, scale->dest->width * 4);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+vs_image_scale_lanczos_AYUV64_double (const VSImage * dest, const VSImage * src,
|
||||
+ uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
|
||||
+ double sharpen)
|
||||
+{
|
||||
+ Scale s = { 0 };
|
||||
+ Scale *scale = &s;
|
||||
+ int n_taps;
|
||||
+
|
||||
+ scale->dest = dest;
|
||||
+ scale->src = src;
|
||||
+
|
||||
+ n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
|
||||
+ scale1d_calculate_taps (&scale->x_scale1d,
|
||||
+ src->width, dest->width, n_taps, a, sharpness, sharpen);
|
||||
+
|
||||
+ n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
|
||||
+ scale1d_calculate_taps (&scale->y_scale1d,
|
||||
+ src->height, dest->height, n_taps, a, sharpness, sharpen);
|
||||
+
|
||||
+ scale->dither = dither;
|
||||
+
|
||||
+ scale->horiz_resample_func =
|
||||
+ (HorizResampleFunc) resample_horiz_double_ayuv_generic;
|
||||
+
|
||||
+ scale->tmpdata =
|
||||
+ g_malloc (sizeof (double) * scale->dest->width * scale->src->height * 4);
|
||||
+
|
||||
+ vs_scale_lanczos_AYUV64_double (scale);
|
||||
+
|
||||
+ scale1d_cleanup (&scale->x_scale1d);
|
||||
+ scale1d_cleanup (&scale->y_scale1d);
|
||||
+ g_free (scale->tmpdata);
|
||||
+}
|
||||
--
|
||||
cgit v0.9.0.2-2-gbebe
|
||||
From f8dc679ca7e9542e6f410062df5e332fc8e0ba9d Mon Sep 17 00:00:00 2001
|
||||
From: David Schleef <ds@schleef.org>
|
||||
Date: Sun, 19 Feb 2012 08:03:03 +0000
|
||||
Subject: videoscale: fix AYUV64 scaling
|
||||
|
||||
---
|
||||
diff --git a/gst/videoscale/vs_lanczos.c b/gst/videoscale/vs_lanczos.c
|
||||
index d141a01..67cd401 100644
|
||||
--- a/gst/videoscale/vs_lanczos.c
|
||||
+++ b/gst/videoscale/vs_lanczos.c
|
||||
@@ -728,6 +728,9 @@ RESAMPLE_HORIZ_AYUV_FLOAT (resample_horiz_double_ayuv_generic, double, double,
|
||||
RESAMPLE_HORIZ_AYUV_FLOAT (resample_horiz_float_ayuv_generic, float, float,
|
||||
guint8, n_taps)
|
||||
|
||||
+RESAMPLE_HORIZ_AYUV_FLOAT (resample_horiz_double_ayuv_generic_s16, double, double,
|
||||
+ guint16, n_taps)
|
||||
+
|
||||
RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_generic, gint32, gint32,
|
||||
guint8, n_taps, shift)
|
||||
RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_generic, gint16, gint16,
|
||||
@@ -1636,7 +1639,7 @@ vs_image_scale_lanczos_AYUV64_double (const VSImage * dest, const VSImage * src,
|
||||
scale->dither = dither;
|
||||
|
||||
scale->horiz_resample_func =
|
||||
- (HorizResampleFunc) resample_horiz_double_ayuv_generic;
|
||||
+ (HorizResampleFunc) resample_horiz_double_ayuv_generic_s16;
|
||||
|
||||
scale->tmpdata =
|
||||
g_malloc (sizeof (double) * scale->dest->width * scale->src->height * 4);
|
||||
--
|
||||
cgit v0.9.0.2-2-gbebe
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
|
||||
---
|
||||
diff --git a/gst-libs/gst/rtsp/gstrtspmessage.c b/gst-libs/gst/rtsp/gstrtspmessage.c
|
||||
index 2b33c61..585d126 100644
|
||||
--- a/gst-libs/gst/rtsp/gstrtspmessage.c
|
||||
+++ b/gst-libs/gst/rtsp/gstrtspmessage.c
|
||||
@@ -183,7 +183,13 @@ gst_rtsp_message_init_request (GstRTSPMessage * msg, GstRTSPMethod method,
|
||||
|
||||
msg->type = GST_RTSP_MESSAGE_REQUEST;
|
||||
msg->type_data.request.method = method;
|
||||
+
|
||||
msg->type_data.request.uri = g_strdup (uri);
|
||||
+ guint len = strlen(msg->type_data.request.uri);
|
||||
+ if(msg->type_data.request.uri[len-1] == '/') {
|
||||
+ msg->type_data.request.uri[len-1] = 0;
|
||||
+ }
|
||||
+
|
||||
msg->type_data.request.version = GST_RTSP_VERSION_1_0;
|
||||
msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
|
|
@ -0,0 +1,23 @@
|
|||
From 0cb4ccb1f09d39820682e052fc106f5fd7fa1309 Mon Sep 17 00:00:00 2001
|
||||
From: Stefan Sauer <ensonic@users.sf.net>
|
||||
Date: Fri, 24 Feb 2012 20:37:00 +0000
|
||||
Subject: interfaces: fix ABI class padding after the recent changes
|
||||
|
||||
---
|
||||
(limited to 'gst-libs/gst/interfaces/colorbalance.h')
|
||||
|
||||
diff --git a/gst-libs/gst/interfaces/colorbalance.h b/gst-libs/gst/interfaces/colorbalance.h
|
||||
index fd8ceaa..9f0a1cd 100644
|
||||
--- a/gst-libs/gst/interfaces/colorbalance.h
|
||||
+++ b/gst-libs/gst/interfaces/colorbalance.h
|
||||
@@ -102,7 +102,7 @@ struct _GstColorBalanceClass {
|
||||
GstColorBalanceType (*get_balance_type) (GstColorBalance *balance);
|
||||
|
||||
/*< private >*/
|
||||
- gpointer _gst_reserved[GST_PADDING];
|
||||
+ gpointer _gst_reserved[GST_PADDING-1];
|
||||
};
|
||||
|
||||
GType gst_color_balance_get_type (void);
|
||||
--
|
||||
cgit v0.9.0.2-2-gbebe
|
|
@ -0,0 +1,22 @@
|
|||
From 7f22e3ea7f713867e1fbf2ef71b6a6e36e1f0531 Mon Sep 17 00:00:00 2001
|
||||
From: Jonathan Liu <net147@gmail.com>
|
||||
Date: Sun, 28 Oct 2012 10:07:16 +0000
|
||||
Subject: oggstream: fix crash with 0 byte ogg packets
|
||||
|
||||
https://bugzilla.gnome.org/show_bug.cgi?id=687030
|
||||
---
|
||||
diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c
|
||||
index c79f088..fe28f2e 100644
|
||||
--- a/ext/ogg/gstoggstream.c
|
||||
+++ b/ext/ogg/gstoggstream.c
|
||||
@@ -790,7 +790,7 @@ setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
|
||||
static gboolean
|
||||
is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
|
||||
{
|
||||
- if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0)
|
||||
+ if (packet->bytes == 0 || (packet->packet[0] & 0x01) == 0)
|
||||
return FALSE;
|
||||
|
||||
if (packet->packet[0] == 5) {
|
||||
--
|
||||
cgit v0.9.0.2-2-gbebe
|
|
@ -0,0 +1,10 @@
|
|||
--- gst-plugins-base/win32/common/libgstaudio.def.orig 2013-12-10 12:09:41.280012610 +0000
|
||||
+++ gst-plugins-base/win32/common/libgstaudio.def 2013-12-10 12:10:12.253127720 +0000
|
||||
@@ -24,6 +24,7 @@
|
||||
gst_audio_decoder_get_plc_aware
|
||||
gst_audio_decoder_get_tolerance
|
||||
gst_audio_decoder_get_type
|
||||
+ gst_audio_decoder_merge_tags
|
||||
gst_audio_decoder_set_byte_time
|
||||
gst_audio_decoder_set_drainable
|
||||
gst_audio_decoder_set_latency
|
|
@ -0,0 +1,122 @@
|
|||
From fece626254e3b250e4514d1c92ac8289421d9511 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
Date: Thu, 15 Mar 2012 11:34:22 +0000
|
||||
Subject: Disable old playbin and decodebin
|
||||
|
||||
---
|
||||
diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am
|
||||
index a0f1abc..700f581 100644
|
||||
--- a/gst/playback/Makefile.am
|
||||
+++ b/gst/playback/Makefile.am
|
||||
@@ -6,19 +6,15 @@ glib_gen_basename = gstplay
|
||||
built_sources = gstplay-marshal.c
|
||||
built_headers = gstplay-marshal.h
|
||||
|
||||
-plugin_LTLIBRARIES = libgstplaybin.la libgstdecodebin.la libgstdecodebin2.la
|
||||
+plugin_LTLIBRARIES = libgstplaybin.la libgstdecodebin2.la
|
||||
|
||||
csp_cflags = -DCOLORSPACE=\"ffmpegcolorspace\"
|
||||
|
||||
libgstplaybin_la_SOURCES = \
|
||||
gstplayback.c \
|
||||
- gstplaybin.c \
|
||||
gstplaybin2.c \
|
||||
gstplaysink.c \
|
||||
- gstplaybasebin.c \
|
||||
gstplay-enum.c \
|
||||
- gststreaminfo.c \
|
||||
- gststreamselector.c \
|
||||
gstsubtitleoverlay.c \
|
||||
gstplaysinkvideoconvert.c \
|
||||
gstplaysinkaudioconvert.c \
|
||||
@@ -37,17 +33,6 @@ if !GST_PLUGIN_BUILD_STATIC
|
||||
libgstplaybin_la_LIBTOOLFLAGS = --tag=disable-static
|
||||
endif
|
||||
|
||||
-libgstdecodebin_la_SOURCES = gstdecodebin.c
|
||||
-nodist_libgstdecodebin_la_SOURCES = $(built_sources)
|
||||
-libgstdecodebin_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(csp_cflags)
|
||||
-libgstdecodebin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
-libgstdecodebin_la_LIBADD = \
|
||||
- $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \
|
||||
- $(GST_LIBS)
|
||||
-if !GST_PLUGIN_BUILD_STATIC
|
||||
-libgstdecodebin_la_LIBTOOLFLAGS = --tag=disable-static
|
||||
-endif
|
||||
-
|
||||
libgstdecodebin2_la_SOURCES = gstdecodebin2.c gsturidecodebin.c gstplay-enum.c
|
||||
nodist_libgstdecodebin2_la_SOURCES = $(built_sources)
|
||||
libgstdecodebin2_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(csp_cflags)
|
||||
@@ -61,11 +46,8 @@ endif
|
||||
|
||||
noinst_HEADERS = \
|
||||
gstplayback.h \
|
||||
- gstplaybasebin.h \
|
||||
gstplaysink.h \
|
||||
- gststreaminfo.h \
|
||||
gstplay-enum.h \
|
||||
- gststreamselector.h \
|
||||
gstrawcaps.h \
|
||||
gstsubtitleoverlay.h \
|
||||
gstplaysinkvideoconvert.h \
|
||||
@@ -83,18 +65,6 @@ include $(top_srcdir)/common/gst-glib-gen.mak
|
||||
|
||||
Android.mk: Makefile.am $(BUILT_SOURCES)
|
||||
androgenizer \
|
||||
- -:PROJECT libgstdecodebin -:SHARED libgstdecodebin \
|
||||
- -:TAGS eng debug \
|
||||
- -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
|
||||
- -:SOURCES $(libgstdecodebin_la_SOURCES) \
|
||||
- $(nodist_libgstdecodebin_la_SOURCES) \
|
||||
- -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstdecodebin_la_CFLAGS) $(csp_cflags) \
|
||||
- -:LDFLAGS $(libgstdecodebin_la_LDFLAGS) \
|
||||
- $(libgstdecodebin_la_LIBADD) \
|
||||
- -ldl \
|
||||
- -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
|
||||
- LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \
|
||||
- \
|
||||
-:PROJECT libgstdecodebin2 -:SHARED libgstdecodebin2 \
|
||||
-:TAGS eng debug \
|
||||
-:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
|
||||
diff --git a/gst/playback/gstplayback.c b/gst/playback/gstplayback.c
|
||||
index 5b74786..f99df6e 100644
|
||||
--- a/gst/playback/gstplayback.c
|
||||
+++ b/gst/playback/gstplayback.c
|
||||
@@ -29,8 +29,6 @@
|
||||
|
||||
#include "gstplayback.h"
|
||||
#include "gstplaysink.h"
|
||||
-#include "gststreamselector.h"
|
||||
-#include "gststreaminfo.h"
|
||||
#include "gstsubtitleoverlay.h"
|
||||
|
||||
static gboolean
|
||||
@@ -47,13 +45,7 @@ plugin_init (GstPlugin * plugin)
|
||||
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
||||
#endif /* ENABLE_NLS */
|
||||
|
||||
- /* ref class from a thread-safe context to work around missing bit of
|
||||
- * thread-safety in GObject */
|
||||
- g_type_class_ref (GST_TYPE_STREAM_INFO);
|
||||
- g_type_class_ref (GST_TYPE_STREAM_SELECTOR);
|
||||
-
|
||||
- res = gst_play_bin_plugin_init (plugin);
|
||||
- res &= gst_play_bin2_plugin_init (plugin);
|
||||
+ res = gst_play_bin2_plugin_init (plugin);
|
||||
res &= gst_play_sink_plugin_init (plugin);
|
||||
res &= gst_subtitle_overlay_plugin_init (plugin);
|
||||
|
||||
diff --git a/gst/playback/gstplayback.h b/gst/playback/gstplayback.h
|
||||
index 4be121c..5981cdd 100644
|
||||
--- a/gst/playback/gstplayback.h
|
||||
+++ b/gst/playback/gstplayback.h
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <gst/gst.h>
|
||||
|
||||
gboolean gst_decode_bin_plugin_init (GstPlugin * plugin);
|
||||
-gboolean gst_play_bin_plugin_init (GstPlugin * plugin);
|
||||
gboolean gst_play_bin2_plugin_init (GstPlugin * plugin);
|
||||
|
||||
|
||||
--
|
||||
cgit v0.9.0.2-2-gbebe
|
|
@ -0,0 +1,171 @@
|
|||
From 63d1316c0fd4ce22cf4a53f4aa7cb1ca16a07aa8 Mon Sep 17 00:00:00 2001
|
||||
From: Tim-Philipp Müller <tim.muller@collabora.co.uk>
|
||||
Date: Sun, 26 Feb 2012 18:19:57 +0000
|
||||
Subject: videoscale: fix negotiation after addition of new formats and methods
|
||||
|
||||
Now that we no longer support all methods for all formats, we
|
||||
need to cater for that in the transform function: we can't
|
||||
transform formats not supported by the currently-selected
|
||||
mehod.
|
||||
|
||||
make check, folks. It's da bomb.
|
||||
---
|
||||
diff --git a/gst/videoscale/gstvideoscale.c b/gst/videoscale/gstvideoscale.c
|
||||
index 9f072a3..60dd5ff 100644
|
||||
--- a/gst/videoscale/gstvideoscale.c
|
||||
+++ b/gst/videoscale/gstvideoscale.c
|
||||
@@ -424,10 +424,118 @@ gst_video_scale_get_property (GObject * object, guint prop_id, GValue * value,
|
||||
}
|
||||
}
|
||||
|
||||
+#define NEAREST (1 << GST_VIDEO_SCALE_NEAREST)
|
||||
+#define BILINEAR (1 << GST_VIDEO_SCALE_BILINEAR)
|
||||
+#define FOURTAP (1 << GST_VIDEO_SCALE_4TAP)
|
||||
+#define LANCZOS (1 << GST_VIDEO_SCALE_LANCZOS)
|
||||
+
|
||||
+/* or we could just do lookups via table[format] if we could be bothered.. */
|
||||
+static const struct
|
||||
+{
|
||||
+ GstVideoFormat format;
|
||||
+ guint8 methods;
|
||||
+} formats_methods_table[] = {
|
||||
+ {
|
||||
+ GST_VIDEO_FORMAT_RGBx, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_xRGB, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_BGRx, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_xBGR, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_RGBA, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_ARGB, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_BGRA, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_ABGR, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_AYUV, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_ARGB64, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_AYUV64, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_RGB, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_BGR, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_v308, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_YUY2, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_YVYU, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_UYVY, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_Y800, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_GRAY8, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_GRAY16_LE, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_GRAY16_BE, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_Y16, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_I420, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_YV12, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_Y444, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_Y42B, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_Y41B, NEAREST | BILINEAR | FOURTAP | LANCZOS}, {
|
||||
+ GST_VIDEO_FORMAT_NV12, NEAREST | BILINEAR}, {
|
||||
+ GST_VIDEO_FORMAT_RGB16, NEAREST | BILINEAR | FOURTAP}, {
|
||||
+ GST_VIDEO_FORMAT_RGB15, NEAREST | BILINEAR | FOURTAP}
|
||||
+};
|
||||
+
|
||||
+static gboolean
|
||||
+gst_video_scale_format_supported_for_method (GstVideoFormat format,
|
||||
+ GstVideoScaleMethod method)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < G_N_ELEMENTS (formats_methods_table); ++i) {
|
||||
+ if (formats_methods_table[i].format == format)
|
||||
+ return ((formats_methods_table[i].methods & (1 << method)) != 0);
|
||||
+ }
|
||||
+ return FALSE;
|
||||
+}
|
||||
+
|
||||
+static gboolean
|
||||
+gst_video_scale_transform_supported (GstVideoScale * videoscale,
|
||||
+ GstVideoScaleMethod method, GstStructure * structure)
|
||||
+{
|
||||
+ const GValue *val;
|
||||
+ GstVideoFormat fmt;
|
||||
+ gboolean supported = TRUE;
|
||||
+ GstStructure *s;
|
||||
+ GstCaps *c;
|
||||
+
|
||||
+ /* we support these methods for all formats */
|
||||
+ if (method == GST_VIDEO_SCALE_NEAREST || method == GST_VIDEO_SCALE_BILINEAR)
|
||||
+ return TRUE;
|
||||
+
|
||||
+ /* we need fixed caps if we want to use gst_video_parse_caps() */
|
||||
+ s = gst_structure_new (gst_structure_get_name (structure),
|
||||
+ "width", G_TYPE_INT, 1, "height", G_TYPE_INT, 1, NULL);
|
||||
+
|
||||
+ if ((val = gst_structure_get_value (structure, "format"))) {
|
||||
+ gst_structure_set_value (s, "format", val);
|
||||
+ } else {
|
||||
+ if ((val = gst_structure_get_value (structure, "endianness")))
|
||||
+ gst_structure_set_value (s, "endianness", val);
|
||||
+ if ((val = gst_structure_get_value (structure, "red_mask")))
|
||||
+ gst_structure_set_value (s, "red_mask", val);
|
||||
+ if ((val = gst_structure_get_value (structure, "blue_mask")))
|
||||
+ gst_structure_set_value (s, "blue_mask", val);
|
||||
+ if ((val = gst_structure_get_value (structure, "green_mask")))
|
||||
+ gst_structure_set_value (s, "green_mask", val);
|
||||
+ if ((val = gst_structure_get_value (structure, "alpha_mask")))
|
||||
+ gst_structure_set_value (s, "alpha_mask", val);
|
||||
+ if ((val = gst_structure_get_value (structure, "depth")))
|
||||
+ gst_structure_set_value (s, "depth", val);
|
||||
+ if ((val = gst_structure_get_value (structure, "bpp")))
|
||||
+ gst_structure_set_value (s, "bpp", val);
|
||||
+ }
|
||||
+ c = gst_caps_new_full (s, NULL);
|
||||
+ if (!gst_video_format_parse_caps (c, &fmt, NULL, NULL)) {
|
||||
+ GST_ERROR_OBJECT (videoscale, "couldn't parse %" GST_PTR_FORMAT, c);
|
||||
+ } else if (!gst_video_scale_format_supported_for_method (fmt, method)) {
|
||||
+ supported = FALSE;
|
||||
+ }
|
||||
+ GST_LOG_OBJECT (videoscale, "method %d %ssupported for format %d",
|
||||
+ method, (supported) ? "" : "not ", fmt);
|
||||
+ gst_caps_unref (c);
|
||||
+
|
||||
+ return supported;
|
||||
+}
|
||||
+
|
||||
static GstCaps *
|
||||
gst_video_scale_transform_caps (GstBaseTransform * trans,
|
||||
GstPadDirection direction, GstCaps * caps)
|
||||
{
|
||||
+ GstVideoScale *videoscale = GST_VIDEO_SCALE (trans);
|
||||
+ GstVideoScaleMethod method;
|
||||
GstCaps *ret;
|
||||
GstStructure *structure;
|
||||
|
||||
@@ -441,6 +549,13 @@ gst_video_scale_transform_caps (GstBaseTransform * trans,
|
||||
ret = gst_caps_copy (caps);
|
||||
structure = gst_structure_copy (gst_caps_get_structure (ret, 0));
|
||||
|
||||
+ GST_OBJECT_LOCK (videoscale);
|
||||
+ method = videoscale->method;
|
||||
+ GST_OBJECT_UNLOCK (videoscale);
|
||||
+
|
||||
+ if (!gst_video_scale_transform_supported (videoscale, method, structure))
|
||||
+ goto format_not_supported;
|
||||
+
|
||||
gst_structure_set (structure,
|
||||
"width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
|
||||
"height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
|
||||
@@ -452,9 +567,19 @@ gst_video_scale_transform_caps (GstBaseTransform * trans,
|
||||
}
|
||||
gst_caps_append_structure (ret, structure);
|
||||
|
||||
+done:
|
||||
+
|
||||
GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
|
||||
|
||||
return ret;
|
||||
+
|
||||
+format_not_supported:
|
||||
+ {
|
||||
+ gst_structure_free (structure);
|
||||
+ gst_caps_unref (ret);
|
||||
+ ret = gst_caps_new_empty ();
|
||||
+ goto done;
|
||||
+ }
|
||||
}
|
||||
|
||||
static gboolean
|
||||
--
|
||||
cgit v0.9.0.2-2-gbebe
|
Binary file not shown.
Before Width: | Height: | Size: 425 KiB After Width: | Height: | Size: 550 KiB |
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
Binary file not shown.
260
ini/base.ini
260
ini/base.ini
|
@ -1,238 +1,132 @@
|
|||
# Basic Feeds Settings
|
||||
# Copyright by Peter Maersk-Moller 2012 - All rights reserved
|
||||
#verbose
|
||||
require version 0.4.2
|
||||
# Basic Feeds Settings for HTT 2015
|
||||
|
||||
verbose
|
||||
|
||||
require version 0.5.1
|
||||
|
||||
# Define allowed remote host IP addresses. If omitted then only 127.0.0.1 will be accepted.
|
||||
system host allow 127.0.0.1
|
||||
system host allow 192.168.11.211
|
||||
|
||||
# Listen on port 9999 for command control connections
|
||||
system control port 9999
|
||||
|
||||
# Set system Geometry to 1024x768
|
||||
system geometry 1024 768 ARGB
|
||||
system geometry 1280 720 ARGB
|
||||
|
||||
# Set system frame rate to 24
|
||||
system frame rate 24
|
||||
system frame rate 25
|
||||
|
||||
# Set output control socket
|
||||
system socket /tmp/mixer1
|
||||
|
||||
# Set feed idle time - feed 0 will always be idle. If no file is given,
|
||||
# idle image will be all black.
|
||||
feed idle 0 1 frames/test_1024x768
|
||||
feed idle 0 1 frames/test_1280x720
|
||||
#feed idle 0 1
|
||||
|
||||
##################
|
||||
# Input feeds
|
||||
##################
|
||||
# Keep size of input feeds to same size as feed 0
|
||||
# otherwise things explode.
|
||||
|
||||
feed add 1 Feed1
|
||||
feed geometry 1 1024 768
|
||||
feed add 1 Cam1
|
||||
feed geometry 1 1280 720
|
||||
feed live 1
|
||||
feed idle 1 100 frames/test_1024x768
|
||||
feed idle 1 100 frames/test_1280x720
|
||||
feed socket 1 /tmp/feed1
|
||||
|
||||
feed add 2 Feed2
|
||||
feed geometry 2 1024 768
|
||||
feed add 2 Cam2
|
||||
feed geometry 2 1280 720
|
||||
feed live 2
|
||||
feed idle 2 100 frames/test_1024x768
|
||||
feed idle 2 100 frames/test_1280x720
|
||||
feed socket 2 /tmp/feed2
|
||||
|
||||
feed add 3 Feed3
|
||||
feed geometry 3 1024 768
|
||||
feed add 3 Cam1
|
||||
feed geometry 3 1280 720
|
||||
feed live 3
|
||||
feed idle 3 100 frames/test_1024x768
|
||||
feed idle 3 100 frames/test_1280x720
|
||||
feed socket 3 /tmp/feed3
|
||||
|
||||
######################
|
||||
# Virtual feeds
|
||||
#######################
|
||||
# Maximized settings for any feed:
|
||||
# virtual feed place rect X 20 20 1024 768 0 0 0.0 0.75 0.75 1.0
|
||||
|
||||
# Feed 1
|
||||
# Starts maximized
|
||||
# Minimized position: 800 20
|
||||
# Minimized size: 205 154
|
||||
virtual feed add 1 VirtualFeed #1
|
||||
virtual feed source feed 1 1
|
||||
virtual feed place rect 1 20 20 1024 768 0 0 0.0 0.75 0.75 1.0
|
||||
###################
|
||||
# Full size vfeeds
|
||||
###################
|
||||
|
||||
# Feed 2
|
||||
# Starts minimized
|
||||
# Minimized position: 800 231
|
||||
# Minimized size: 205 154
|
||||
virtual feed add 2 Feed #2
|
||||
virtual feed source feed 2 2
|
||||
virtual feed place rect 2 800 230 1024 768 0 0 0.0 0.20 0.20 1.0
|
||||
vfeed add 1 FullCam1
|
||||
vfeed source feed 1 1
|
||||
vfeed place rect 1 0 0 1280 720 0 0
|
||||
|
||||
# Feed 3
|
||||
# Starts minimized
|
||||
# Minimized position: 800 442
|
||||
# Minimized size: 205 154
|
||||
virtual feed add 3 VirtualFeed #3
|
||||
virtual feed source feed 3 3
|
||||
virtual feed place rect 3 800 442 1024 768 0 0 0.0 0.20 0.20 1.0
|
||||
vfeed add 2 FullCam1
|
||||
vfeed source feed 2 2
|
||||
vfeed place rect 2 0 0 1280 720 0 0
|
||||
|
||||
vfeed add 3 FullCam1
|
||||
vfeed source feed 3 3
|
||||
vfeed place rect 3 0 0 1280 720 0 0
|
||||
|
||||
|
||||
####################
|
||||
# Small size vfeeds
|
||||
####################
|
||||
|
||||
command create MinimizeFeed1
|
||||
# Since everything from now on is relative,
|
||||
# we should make sure everthings is where we expect it to be
|
||||
virtual feed place rect 1 20 20 1024 768 0 0 0.0 0.75 0.75 1.0
|
||||
vfeed add 4 SmallCam1
|
||||
vfeed source feed 4 1
|
||||
vfeed place rect 4 5 2 1280 720 0 0 0 0.492 0.492 1.0
|
||||
|
||||
# The scale and coor commands are nonblocking
|
||||
virtual feed move scale 1 -0.055 -0.055 10 10
|
||||
virtual feed move coor 1 65 0 12 0
|
||||
vfeed add 5 SmallCam1
|
||||
vfeed source feed 5 2
|
||||
vfeed place rect 5 645 2 1280 720 0 0 0 0.492 0.492 1.0
|
||||
|
||||
# Allow Maximizing
|
||||
command restart MaximizeFeed1
|
||||
command end
|
||||
|
||||
command create MaximizeFeed1
|
||||
# Since everything from now on is relative,
|
||||
# we should make sure everthings is where we expect it to be
|
||||
virtual feed place rect 1 800 20 1024 768 0 0 0.0 0.20 0.20 1.0
|
||||
|
||||
# The scale and coor commands are nonblocking
|
||||
virtual feed move scale 1 0.055 0.055 10 10
|
||||
virtual feed move coor 1 -65 0 12 0
|
||||
|
||||
# Allow Minimizing
|
||||
command restart MinimizeFeed1
|
||||
command end
|
||||
|
||||
command create MinimizeFeed2
|
||||
# Since everything from now on is relative,
|
||||
# we should make sure everthings is where we expect it to be
|
||||
virtual feed place rect 2 20 20 1024 768 0 0 0.0 0.75 0.75 1.0
|
||||
|
||||
# The scale and coor commands are nonblocking
|
||||
virtual feed move scale 2 -0.055 -0.055 10 10
|
||||
virtual feed move coor 2 65 21 12 10
|
||||
|
||||
# Allow Maximizing
|
||||
command restart MaximizeFeed2
|
||||
command end
|
||||
|
||||
command create MaximizeFeed2
|
||||
# Since everything from now on is relative,
|
||||
# we should make sure everthings is where we expect it to be
|
||||
virtual feed place rect 2 800 230 1024 768 0 0 0.0 0.20 0.20 1.0
|
||||
|
||||
# The scale and coor commands are nonblocking
|
||||
virtual feed move scale 2 0.055 0.055 10 10
|
||||
virtual feed move coor 2 -65 -21 12 10
|
||||
|
||||
# Allow Minimizing
|
||||
command restart MinimizeFeed2
|
||||
command end
|
||||
|
||||
command create MinimizeFeed3
|
||||
# Since everything from now on is relative,
|
||||
# we should make sure everthings is where we expect it to be
|
||||
virtual feed place rect 3 20 20 1024 768 0 0 0.0 0.75 0.75 1.0
|
||||
|
||||
# The scale and coor commands are nonblocking
|
||||
virtual feed move scale 3 -0.055 -0.055 10 10
|
||||
virtual feed move coor 3 65 35 12 12
|
||||
|
||||
# Allow Maximizing
|
||||
command restart MaximizeFeed3
|
||||
command end
|
||||
|
||||
command create MaximizeFeed3
|
||||
# Since everything from now on is relative,
|
||||
# we should make sure everthings is where we expect it to be
|
||||
virtual feed place rect 3 800 442 1024 768 0 0 0.0 0.20 0.20 1.0
|
||||
|
||||
# The scale and coor commands are nonblocking
|
||||
virtual feed move scale 3 0.055 0.055 10 10
|
||||
virtual feed move coor 3 -65 -35 12 12
|
||||
|
||||
# Allow Minimizing
|
||||
command restart MinimizeFeed3
|
||||
command end
|
||||
|
||||
command create Swap12
|
||||
MinimizeFeed1
|
||||
MaximizeFeed2
|
||||
command restart Swap21
|
||||
command restart Swap23
|
||||
command end
|
||||
|
||||
command create Swap13
|
||||
MinimizeFeed1
|
||||
MaximizeFeed3
|
||||
command restart Swap31
|
||||
command restart Swap32
|
||||
command end
|
||||
|
||||
command create Swap21
|
||||
MinimizeFeed2
|
||||
MaximizeFeed1
|
||||
command restart Swap12
|
||||
command restart Swap13
|
||||
command end
|
||||
|
||||
command create Swap23
|
||||
MinimizeFeed2
|
||||
MaximizeFeed3
|
||||
command restart Swap31
|
||||
command restart Swap32
|
||||
command end
|
||||
|
||||
command create Swap31
|
||||
MinimizeFeed3
|
||||
MaximizeFeed1
|
||||
command restart Swap12
|
||||
command restart Swap13
|
||||
command end
|
||||
vfeed add 6 SmallCam1
|
||||
vfeed source feed 6 3
|
||||
vfeed place rect 6 645 363 1280 720 0 0 0 0.492 0.492 1.0
|
||||
|
||||
|
||||
command create Swap32
|
||||
MinimizeFeed3
|
||||
MaximizeFeed2
|
||||
command restart Swap21
|
||||
command restart Swap23
|
||||
command end
|
||||
#########
|
||||
# Images
|
||||
#########
|
||||
|
||||
stack 0
|
||||
|
||||
# Load the background
|
||||
image load 0 images/background.png
|
||||
image place 0 0 0 0
|
||||
|
||||
# Load the logo
|
||||
image load 1 images/logo.png
|
||||
image place 1 1 24 24
|
||||
|
||||
text font 1 Source Code Pro Bold 40
|
||||
text string 1 00:00:00
|
||||
text place 1 1 1 200 630 1.0 1.0 1.0
|
||||
|
||||
#Text
|
||||
text font 1 Source Code Pro Bold 32
|
||||
|
||||
text string 1 [Test text 1]
|
||||
text place 1 1 1 32 650 1.0 1.0 1.0
|
||||
|
||||
text string 2 [Test text 2]
|
||||
text place 2 2 1 32 700 1.0 1.0 1.0
|
||||
command create ShowAll
|
||||
image overlay 0
|
||||
vfeed overlay 4..6
|
||||
text overlay 1
|
||||
|
||||
|
||||
command create Show
|
||||
overlay image 0
|
||||
|
||||
overlay virtual feed 1..3
|
||||
|
||||
overlay image 1
|
||||
overlay text all
|
||||
loop
|
||||
loop
|
||||
command end
|
||||
|
||||
overlay finish Show
|
||||
|
||||
command create ShowFull1
|
||||
vfeed overlay 1
|
||||
image overlay 1
|
||||
|
||||
loop
|
||||
command end
|
||||
|
||||
command create ShowFull2
|
||||
vfeed overlay 2
|
||||
image overlay 1
|
||||
|
||||
loop
|
||||
command end
|
||||
|
||||
command create ShowFull3
|
||||
vfeed overlay 3
|
||||
image overlay 1
|
||||
|
||||
loop
|
||||
command end
|
||||
|
||||
|
||||
#montor on
|
||||
overlay finish ShowAll
|
||||
|
||||
#monitor on
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env python2
|
||||
# -*- coding: utf-8 -*-
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
class ClockController(object):
|
||||
|
||||
TEXT_ID = 1
|
||||
|
||||
def __init__(self, socket):
|
||||
self.snowmix_socket = socket
|
||||
|
||||
self.update_thread = threading.Thread(target=self.update_clock)
|
||||
self.stopped = False
|
||||
self.update_thread.start()
|
||||
|
||||
def update_clock(self):
|
||||
print "Starting update thread !"
|
||||
while not self.stopped:
|
||||
now = datetime.now()
|
||||
self.update_text(now.strftime("%H:%M:%S"))
|
||||
time.sleep(1.0)
|
||||
|
||||
print "Stopping update thread !"
|
||||
|
||||
|
||||
def update_text(self, text):
|
||||
cmd = "text string %d %s\n" % (self.TEXT_ID,text)
|
||||
self.snowmix_socket.send(cmd)
|
||||
|
||||
|
||||
def stop(self):
|
||||
self.stopped = True
|
||||
self.update_thread.join(5.0)
|
||||
|
||||
|
||||
|
||||
|
|
@ -3,12 +3,12 @@
|
|||
class SceneChanger(object):
|
||||
|
||||
def __init__(self, socket):
|
||||
self.cur_scene = 1
|
||||
self.cur_scene = 'Full'
|
||||
self.snowmix_socket = socket
|
||||
|
||||
|
||||
def switch_to(self, next_scene):
|
||||
if self.cur_scene != next_scene:
|
||||
cmd = "Swap%d%d\n" % (self.cur_scene, next_scene)
|
||||
cmd = "overlay finish Show%s\n" % (next_scene)
|
||||
self.snowmix_socket.send(cmd)
|
||||
self.cur_scene = next_scene
|
|
@ -6,20 +6,18 @@ import socket
|
|||
from gi.repository import Gtk, GObject
|
||||
|
||||
from scenechanger import SceneChanger
|
||||
from textchanger import TextChanger
|
||||
from timercontroller import TimerController
|
||||
from clockcontroller import ClockController
|
||||
|
||||
class VideoController(object):
|
||||
def __init__(self, ip):
|
||||
|
||||
|
||||
self.snowmix_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.snowmix_socket.connect((ip, 9999))
|
||||
|
||||
self.scenechanger = SceneChanger(self.snowmix_socket)
|
||||
self.textchanger = TextChanger(self.snowmix_socket)
|
||||
self.timercontroller = TimerController(self.snowmix_socket)
|
||||
self.clockcontroller = ClockController(self.snowmix_socket)
|
||||
|
||||
self.gladefile = "videocontroller.ui"
|
||||
self.gladefile = "videocontroller.ui"
|
||||
self.builder = Gtk.Builder()
|
||||
self.builder.add_from_file(self.gladefile)
|
||||
|
||||
|
@ -27,9 +25,7 @@ class VideoController(object):
|
|||
self.max_cam1_button = self.builder.get_object("MaxCam1Button")
|
||||
self.max_cam2_button = self.builder.get_object("MaxCam2Button")
|
||||
self.max_cam3_button = self.builder.get_object("MaxCam3Button")
|
||||
self.set_text_entry = self.builder.get_object("SetTextEntry")
|
||||
self.start_timer_entry = self.builder.get_object("StartTimerEntry")
|
||||
self.change_timer_combobox = self.builder.get_object("ChangeTimerComboBox")
|
||||
self.show_all_button = self.builder.get_object("ShowAllButton")
|
||||
|
||||
self.messagebox = Gtk.MessageDialog(self.main_window, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, "I'm afaraid I can't let you do that dave.")
|
||||
|
||||
|
@ -37,24 +33,24 @@ class VideoController(object):
|
|||
self.main_window.show_all()
|
||||
|
||||
|
||||
|
||||
def MaxCam1ButtonToggled(self, widget):
|
||||
if self.max_cam1_button.get_active():
|
||||
self.max_cam2_button.set_active(False)
|
||||
self.max_cam3_button.set_active(False)
|
||||
self.scenechanger.switch_to(1)
|
||||
self.show_all_button.set_active(False)
|
||||
self.scenechanger.switch_to('Full1')
|
||||
else:
|
||||
if not self.max_cam2_button.get_active() and not self.max_cam3_button.get_active():
|
||||
if not self.max_cam2_button.get_active() and not self.max_cam3_button.get_active() and not self.show_all_button.get_active():
|
||||
self.max_cam1_button.set_active(True)
|
||||
|
||||
|
||||
def MaxCam2ButtonToggled(self, widget):
|
||||
if self.max_cam2_button.get_active():
|
||||
self.max_cam1_button.set_active(False)
|
||||
self.max_cam3_button.set_active(False)
|
||||
self.scenechanger.switch_to(2)
|
||||
self.show_all_button.set_active(False)
|
||||
self.scenechanger.switch_to('Full2')
|
||||
else:
|
||||
if not self.max_cam1_button.get_active() and not self.max_cam3_button.get_active():
|
||||
if not self.max_cam1_button.get_active() and not self.max_cam3_button.get_active() and not self.show_all_button.get_active():
|
||||
self.max_cam2_button.set_active(True)
|
||||
|
||||
|
||||
|
@ -62,40 +58,28 @@ class VideoController(object):
|
|||
if self.max_cam3_button.get_active():
|
||||
self.max_cam1_button.set_active(False)
|
||||
self.max_cam2_button.set_active(False)
|
||||
self.scenechanger.switch_to(3)
|
||||
self.show_all_button.set_active(False)
|
||||
self.scenechanger.switch_to('Full3')
|
||||
else:
|
||||
if not self.max_cam1_button.get_active() and not self.max_cam2_button.get_active():
|
||||
if not self.max_cam1_button.get_active() and not self.max_cam2_button.get_active() and not self.show_all_button.get_active():
|
||||
self.max_cam3_button.set_active(True)
|
||||
|
||||
|
||||
def SetTextButtonClicked(self, widget):
|
||||
self.textchanger.set_text(self.set_text_entry.get_text())
|
||||
|
||||
|
||||
def HideTextButtonClicked(self, widget):
|
||||
self.textchanger.hide_text()
|
||||
|
||||
|
||||
def StartTimerButtonClicked(self, widget):
|
||||
self.timercontroller.start_timer(self.start_timer_entry.get_text())
|
||||
self.change_timer_combobox.remove_all()
|
||||
for timer in self.timercontroller.get_timers():
|
||||
self.change_timer_combobox.append_text(timer)
|
||||
|
||||
def ChangeTimerComboBoxChanged(self, widget):
|
||||
self.timercontroller.set_active_timer(self.change_timer_combobox.get_active_text())
|
||||
|
||||
|
||||
def HideTimerButtonClicked(self, widget):
|
||||
self.timercontroller.hide_timer()
|
||||
|
||||
def ShowAllButtonToggled(self, widget):
|
||||
if self.show_all_button.get_active():
|
||||
self.max_cam1_button.set_active(False)
|
||||
self.max_cam2_button.set_active(False)
|
||||
self.max_cam3_button.set_active(False)
|
||||
self.scenechanger.switch_to('All')
|
||||
else:
|
||||
if not self.max_cam1_button.get_active() and not self.max_cam2_button.get_active() and not self.max_cam3_button.get_active():
|
||||
self.show_all_button.set_active(True)
|
||||
|
||||
|
||||
def MainWindowDelete(self, widget, event):
|
||||
if self.messagebox.run() == Gtk.ResponseType.OK:
|
||||
self.timercontroller.stop()
|
||||
self.clockcontroller.stop()
|
||||
Gtk.main_quit()
|
||||
|
||||
|
||||
self.messagebox.hide()
|
||||
return True
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.12"/>
|
||||
<object class="GtkWindow" id="MainWindow">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="opacity">0.92000000000000004</property>
|
||||
<property name="title" translatable="yes">Video Controller</property>
|
||||
<property name="default_width">443</property>
|
||||
<property name="default_height">250</property>
|
||||
<signal name="delete-event" handler="MainWindowDelete" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkLayout" id="layout1">
|
||||
<property name="width_request">490</property>
|
||||
<property name="height_request">370</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="width">0</property>
|
||||
<property name="height">0</property>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="MaxCam3Button">
|
||||
<property name="label" translatable="yes">Maximize
|
||||
Cam 3</property>
|
||||
<property name="width_request">150</property>
|
||||
<property name="height_request">150</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="toggled" handler="MaxCam3ButtonToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">330</property>
|
||||
<property name="y">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="MaxCam1Button">
|
||||
<property name="label" translatable="yes">Maximize
|
||||
Cam1</property>
|
||||
<property name="width_request">150</property>
|
||||
<property name="height_request">150</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="toggled" handler="MaxCam1ButtonToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">10</property>
|
||||
<property name="y">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="MaxCam2Button">
|
||||
<property name="label" translatable="yes">Maximize
|
||||
Cam 2</property>
|
||||
<property name="width_request">150</property>
|
||||
<property name="height_request">150</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="toggled" handler="MaxCam2ButtonToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">170</property>
|
||||
<property name="y">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="ShowAllButton">
|
||||
<property name="label" translatable="yes">Show alll
|
||||
Cameras</property>
|
||||
<property name="width_request">470</property>
|
||||
<property name="height_request">150</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="relief">half</property>
|
||||
<property name="yalign">0.4699999988079071</property>
|
||||
<signal name="toggled" handler="ShowAllButtonToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">10</property>
|
||||
<property name="y">200</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,59 @@
|
|||
#!/bin/env python2
|
||||
import os
|
||||
import threading
|
||||
from time import sleep
|
||||
|
||||
import gi
|
||||
gi.require_version('Gst', '1.0')
|
||||
from gi.repository import Gst
|
||||
|
||||
from feed import Feed
|
||||
|
||||
from config import *
|
||||
|
||||
class CameraFeed(Feed):
|
||||
def __init__(self, feed_pipe, camera_ip):
|
||||
super(CameraFeed, self).__init__(feed_pipe)
|
||||
|
||||
self._feed_pipe = feed_pipe
|
||||
self._camera_ip = camera_ip
|
||||
|
||||
def start(self):
|
||||
if self.is_running():
|
||||
return
|
||||
|
||||
if not os.path.exists(MIXER_PIPE):
|
||||
print '[%s] not starting because mixer is not running (pipe is missing)' % self._name
|
||||
return
|
||||
|
||||
if os.path.exists(self._feed_pipe):
|
||||
print '[%s] not starting because feed pipe already exists' % self._name
|
||||
return
|
||||
|
||||
print '[%s] is starting' % self._name
|
||||
self._running = True
|
||||
self._thread = threading.Thread(target=self._run)
|
||||
self._thread.start()
|
||||
|
||||
|
||||
def _run(self):
|
||||
src = FEED_SOURCE % {'ip': self._camera_ip}
|
||||
mixer_format = MIXER_FORMAT % {'width' : MIXER_WIDTH, 'height' : MIXER_HEIGHT, 'framerate' : MIXER_FRAMERATE}
|
||||
sink = FEED_SINK % {'feed_pipe' : self._feed_pipe, 'shm_size' : SHM_SIZE}
|
||||
|
||||
print '%s ! %s ! %s ! %s' % (src, SCALE, mixer_format, sink)
|
||||
|
||||
self._pipeline = Gst.parse_launch('%s ! %s ! %s ! %s' % (src, SCALE, mixer_format, sink))
|
||||
|
||||
self._pipeline.set_state(Gst.State.PLAYING)
|
||||
print "[%s] is playing" % self._name
|
||||
|
||||
bus = self._pipeline.get_bus()
|
||||
msg = bus.timed_pop_filtered(Gst.CLOCK_TIME_NONE, Gst.MessageType.ERROR | Gst.MessageType.EOS)
|
||||
print "[%s] %s" % (self._name, msg.parse_error()[1])
|
||||
|
||||
self._pipeline.set_state(Gst.State.NULL)
|
||||
|
||||
self._running = False
|
||||
|
||||
print "[%s] has stopped" % self._name
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/env python2
|
||||
|
||||
CAMERA_FEEDS = {
|
||||
'/tmp/feed1' : '192.168.2.20',
|
||||
'/tmp/feed2' : '192.168.2.30',
|
||||
'/tmp/feed3' : '192.168.2.40',
|
||||
}
|
||||
|
||||
MIXER_PIPE = '/tmp/mixer1'
|
||||
|
||||
MIXER_WIDTH = 1280
|
||||
MIXER_HEIGHT = 720
|
||||
MIXER_FRAMERATE = 25
|
||||
SHM_SIZE = MIXER_WIDTH * MIXER_WIDTH * 4 * 26
|
||||
|
||||
SCREEN_WIDTH = 640
|
||||
SCREEN_HEIGHT = 360
|
||||
OUTPUT_PORT = 6666
|
||||
|
||||
|
||||
|
||||
FEED_SOURCE = 'rtspsrc location=rtspt://%(ip)s ! rtph264depay ! h264parse ! avdec_h264'
|
||||
MIXER_FORMAT = 'video/x-raw, format=BGRA,'\
|
||||
+ ' width=%(width)d, height=%(height)d, framerate=%(framerate)d/1, pixel-aspect-ratio=1/1'
|
||||
SCALE = 'videorate ! videoscale ! videoconvert'
|
||||
FEED_SINK = 'shmsink socket-path=%(feed_pipe)s shm-size=%(shm_size)d wait-for-connection=1 sync=true'
|
||||
|
||||
OUTPUT_SOURCE = 'shmsrc socket-path=%(mixer_pipe)s do-timestamp=true is-live=true'
|
||||
SCREEN_OUTPUT = 'videoscale ! video/x-raw, format=BGRA, width=%(screen_width)d, height=%(screen_height)d !'\
|
||||
+ 'videoconvert ! timeoverlay ! ximagesink'
|
||||
NETWORK_OUTPUT = 'videoconvert ! x264enc tune="zerolatency" ! video/x-h264, profile="high" ! mpegtsmux ! tcpserversink host=0.0.0.0 port=%(port)d'
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/env python2
|
||||
|
||||
import gi
|
||||
gi.require_version('Gst', '1.0')
|
||||
from gi.repository import Gst
|
||||
|
||||
class Feed(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self._running = False
|
||||
self._thead = None
|
||||
self._pipeline = None
|
||||
self._name = name
|
||||
|
||||
|
||||
def is_running(self):
|
||||
return self._running
|
||||
|
||||
|
||||
def stop(self):
|
||||
if not self._running:
|
||||
return
|
||||
|
||||
if self._pipeline <> None:
|
||||
print "[%s] pipeline stopped" % self._name
|
||||
self._pipeline.set_state(Gst.State.NULL)
|
||||
|
||||
if self._thread <> None:
|
||||
print "[%s] waiting for thread to terminate" % self._name
|
||||
self._thread.join(2.0)
|
|
@ -0,0 +1,61 @@
|
|||
#!/bin/env python2
|
||||
import os
|
||||
import threading
|
||||
from time import sleep
|
||||
|
||||
import gi
|
||||
gi.require_version('Gst', '1.0')
|
||||
from gi.repository import Gst
|
||||
|
||||
from feed import Feed
|
||||
|
||||
from config import *
|
||||
|
||||
|
||||
class OutputFeed(Feed):
|
||||
def __init__(self):
|
||||
super(OutputFeed, self).__init__(MIXER_PIPE)
|
||||
|
||||
|
||||
def start(self):
|
||||
if self.is_running():
|
||||
return
|
||||
|
||||
if not os.path.exists(MIXER_PIPE):
|
||||
print '[%s] not starting because mixer is not running (pipe is missing)' % self._name
|
||||
return
|
||||
|
||||
|
||||
print '[%s] is starting' % self._name
|
||||
self._running = True
|
||||
self._thread = threading.Thread(target=self._run)
|
||||
self._thread.start()
|
||||
|
||||
|
||||
def _run(self):
|
||||
src = OUTPUT_SOURCE % {'mixer_pipe' : MIXER_PIPE}
|
||||
mixer_format = MIXER_FORMAT % {'width' : MIXER_WIDTH, 'height' : MIXER_HEIGHT, 'framerate' : MIXER_FRAMERATE}
|
||||
screen_output = SCREEN_OUTPUT % {'screen_width' : SCREEN_WIDTH, 'screen_height' : SCREEN_HEIGHT}
|
||||
network_output = NETWORK_OUTPUT % {'port' : OUTPUT_PORT}
|
||||
|
||||
pipeline = '%s ! %s ! queue leaky=2 ! tee name=split ! queue leaky=2 ! %s split. ! queue leaky=2 ! %s' % (src,
|
||||
mixer_format,
|
||||
screen_output,
|
||||
network_output)
|
||||
print pipeline
|
||||
self._pipeline = Gst.parse_launch(pipeline)
|
||||
|
||||
|
||||
|
||||
self._pipeline.set_state(Gst.State.PLAYING)
|
||||
print "[%s] is playing" % self._name
|
||||
|
||||
bus = self._pipeline.get_bus()
|
||||
msg = bus.timed_pop_filtered(Gst.CLOCK_TIME_NONE, Gst.MessageType.ERROR | Gst.MessageType.EOS)
|
||||
print "[%s] %s" % (self._name, msg.parse_error()[1])
|
||||
|
||||
self._pipeline.set_state(Gst.State.NULL)
|
||||
|
||||
self._running = False
|
||||
|
||||
print "[%s] has stopped" % self._name
|
|
@ -0,0 +1,45 @@
|
|||
#!/bin/env python2
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
import time
|
||||
|
||||
import gi
|
||||
gi.require_version('Gst', '1.0')
|
||||
from gi.repository import Gst,GObject
|
||||
|
||||
from camerafeed import CameraFeed
|
||||
from outputfeed import OutputFeed
|
||||
|
||||
from config import *
|
||||
|
||||
feeds = []
|
||||
|
||||
def teardown():
|
||||
print "[main] Stopping all remaining feeds"
|
||||
for feed in feeds:
|
||||
feed.stop()
|
||||
|
||||
def handle_sigint(signum, frame):
|
||||
print "[main] caught sigint"
|
||||
teardown()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
GObject.threads_init()
|
||||
Gst.init(None)
|
||||
signal.signal(signal.SIGINT, handle_sigint)
|
||||
|
||||
for pipe, ip in CAMERA_FEEDS.items():
|
||||
feeds.append(CameraFeed(pipe, ip))
|
||||
|
||||
feeds.append(OutputFeed())
|
||||
|
||||
while os.path.exists(MIXER_PIPE):
|
||||
for feed in feeds:
|
||||
if not feed.is_running():
|
||||
feed.start()
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
teardown()
|
|
@ -1,29 +0,0 @@
|
|||
#!/bin/env python2
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class TextChanger(object):
|
||||
|
||||
TEXT_ID = 1
|
||||
UMLAUTS = {
|
||||
'ä' : 'ae',
|
||||
'ü' : 'ue',
|
||||
'ö' : 'oe',
|
||||
'ß' : 'ss'
|
||||
}
|
||||
|
||||
def __init__(self, socket):
|
||||
self.snowmix_socket = socket
|
||||
|
||||
|
||||
def set_text(self, text):
|
||||
for umlaut, replacement in self.UMLAUTS.items():
|
||||
text = text.replace(umlaut, replacement)
|
||||
|
||||
cmd = "text string %d %s\n" % (self.TEXT_ID,text)
|
||||
self.snowmix_socket.send(cmd)
|
||||
|
||||
def hide_text(self):
|
||||
cmd = "text string %d\n" % self.TEXT_ID
|
||||
self.snowmix_socket.send(cmd)
|
||||
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
#!/usr/bin/env python2
|
||||
# -*- coding: utf-8 -*-
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
class TimerController(object):
|
||||
|
||||
TEXT_ID = 2
|
||||
UMLAUTS = {
|
||||
'ä' : 'ae',
|
||||
'ü' : 'ue',
|
||||
'ö' : 'oe',
|
||||
'ß' : 'ss'
|
||||
}
|
||||
|
||||
|
||||
|
||||
def __init__(self, socket):
|
||||
self.snowmix_socket = socket
|
||||
|
||||
self.active_timer = None
|
||||
self.timers = {}
|
||||
|
||||
self.update_thread = threading.Thread(target=self.update_timer)
|
||||
self.stopped = False
|
||||
self.update_thread.start()
|
||||
|
||||
def start_timer(self, name):
|
||||
if not name in self.timers.keys():
|
||||
self.timers[name] = datetime.now()
|
||||
|
||||
def get_timers(self):
|
||||
return self.timers.keys()
|
||||
|
||||
|
||||
def hide_timer(self):
|
||||
self.active_timer = None
|
||||
self.hide_text()
|
||||
|
||||
|
||||
def set_active_timer(self, name):
|
||||
if name in self.timers.keys():
|
||||
self.active_timer = name
|
||||
|
||||
|
||||
def update_timer(self):
|
||||
print "Starting update thread !"
|
||||
while not self.stopped:
|
||||
if self.active_timer != None:
|
||||
delta = datetime.now() - self.timers[self.active_timer]
|
||||
hours, remainder = divmod(delta.total_seconds(), 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
self.update_text('%s: %d:%d:%d' % (self.active_timer, hours, minutes, seconds))
|
||||
pass
|
||||
time.sleep(1.0)
|
||||
|
||||
print "Stopping update thread !"
|
||||
|
||||
|
||||
def update_text(self, text):
|
||||
for umlaut, replacement in self.UMLAUTS.items():
|
||||
text = text.replace(umlaut, replacement)
|
||||
|
||||
cmd = "text string %d %s\n" % (self.TEXT_ID,text)
|
||||
self.snowmix_socket.send(cmd)
|
||||
|
||||
|
||||
def hide_text(self):
|
||||
cmd = "text string %d\n" % self.TEXT_ID
|
||||
self.snowmix_socket.send(cmd)
|
||||
|
||||
|
||||
def stop(self):
|
||||
self.stopped = True
|
||||
self.update_thread.join(5.0)
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.12"/>
|
||||
<object class="GtkWindow" id="MainWindow">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="opacity">0.92000000000000004</property>
|
||||
<property name="title" translatable="yes">Video Controller</property>
|
||||
<property name="default_width">443</property>
|
||||
<property name="default_height">250</property>
|
||||
<signal name="delete-event" handler="MainWindowDelete" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkLayout" id="layout1">
|
||||
<property name="width_request">490</property>
|
||||
<property name="height_request">370</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="width">0</property>
|
||||
<property name="height">0</property>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="MaxCam3Button">
|
||||
<property name="label" translatable="yes">Maximize
|
||||
Cam 3</property>
|
||||
<property name="width_request">150</property>
|
||||
<property name="height_request">150</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="toggled" handler="MaxCam3ButtonToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">330</property>
|
||||
<property name="y">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="MaxCam1Button">
|
||||
<property name="label" translatable="yes">Maximize
|
||||
Cam1</property>
|
||||
<property name="width_request">150</property>
|
||||
<property name="height_request">150</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="toggled" handler="MaxCam1ButtonToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">10</property>
|
||||
<property name="y">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="MaxCam2Button">
|
||||
<property name="label" translatable="yes">Maximize
|
||||
Cam 2</property>
|
||||
<property name="width_request">150</property>
|
||||
<property name="height_request">150</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="toggled" handler="MaxCam2ButtonToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">170</property>
|
||||
<property name="y">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="SetTextLabel">
|
||||
<property name="width_request">130</property>
|
||||
<property name="height_request">30</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">3</property>
|
||||
<property name="label" translatable="yes">Set text</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">10</property>
|
||||
<property name="y">170</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="SetTextButton">
|
||||
<property name="label" translatable="yes">Set Text</property>
|
||||
<property name="width_request">120</property>
|
||||
<property name="height_request">30</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="SetTextButtonClicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">340</property>
|
||||
<property name="y">174</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="StartTimerLabel">
|
||||
<property name="width_request">130</property>
|
||||
<property name="height_request">30</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Start a new timer</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">10</property>
|
||||
<property name="y">220</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="StartTimerEntry">
|
||||
<property name="width_request">180</property>
|
||||
<property name="height_request">30</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">150</property>
|
||||
<property name="y">220</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="SetTextEntry">
|
||||
<property name="width_request">180</property>
|
||||
<property name="height_request">30</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">150</property>
|
||||
<property name="y">174</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="StartTimerButton">
|
||||
<property name="label" translatable="yes">Start Timer</property>
|
||||
<property name="width_request">120</property>
|
||||
<property name="height_request">30</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="StartTimerButtonClicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">340</property>
|
||||
<property name="y">220</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="ChangeTimerLabel">
|
||||
<property name="width_request">150</property>
|
||||
<property name="height_request">30</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Change timer dispaly</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">10</property>
|
||||
<property name="y">270</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="HideTextButton">
|
||||
<property name="label" translatable="yes">Hide text</property>
|
||||
<property name="width_request">200</property>
|
||||
<property name="height_request">30</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="HideTextButtonClicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">10</property>
|
||||
<property name="y">320</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="HideTimer">
|
||||
<property name="label" translatable="yes">Hide Timer</property>
|
||||
<property name="width_request">200</property>
|
||||
<property name="height_request">30</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="HideTimerButtonClicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">230</property>
|
||||
<property name="y">320</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="ChangeTimerComboBox">
|
||||
<property name="width_request">285</property>
|
||||
<property name="height_request">30</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<signal name="changed" handler="ChangeTimerComboBoxChanged" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x">174</property>
|
||||
<property name="y">270</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
./cam2feed.sh /tmp/feed1 "http://gstreamer:GlytEnru@192.168.50.100/videostream.cgi?rate=0"
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
./cam2feed.sh /tmp/feed2 "http://gstreamer:GlytEnru@192.168.50.101/videostream.cgi?rate=0"
|
|
@ -1,28 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Deliver a webcam video as input feed
|
||||
|
||||
if [ -z "$1" -o -z "$2" ]; then
|
||||
echo "Usage $0 <control pipe> <cam url>"
|
||||
exit
|
||||
fi
|
||||
|
||||
CONTROL_PIPE="$1"
|
||||
MIXERFORMAT='video/x-raw-rgb, bpp=(int)32, depth=32, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)1024, height=(int)768, framerate=(fraction)24/1, pixel-aspect-ratio=(fraction)1/1, interlaced=(boolean)false'
|
||||
SRC="souphttpsrc location=$2 ! jpegdec ! videorate"
|
||||
SHMSIZE='shm-size=10000000'
|
||||
SHMOPTION="wait-for-connection=0"
|
||||
SHMSINK1="shmsink socket-path=$CONTROL_PIPE $SHMSIZE $SHMOPTION"
|
||||
SCALE='ffmpegcolorspace ! videoscale ! ffmpegcolorspace'
|
||||
|
||||
|
||||
while true ; do
|
||||
# Remove the named pipe if it exist
|
||||
rm -f $CONTROL_PIPE
|
||||
gst-launch-0.10 -v \
|
||||
$SRC ! \
|
||||
$SCALE ! \
|
||||
$MIXERFORMAT ! \
|
||||
$SHMSINK1
|
||||
sleep 2
|
||||
done
|
||||
exit
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
./cam2feed.sh /tmp/feed3 "http://gstreamer:GlytEnru@192.168.50.102/videostream.cgi?rate=0"
|
|
@ -1,4 +0,0 @@
|
|||
MIXERCAPS='video/x-raw-rgb, width=(int)1024, height=(int)768'
|
||||
|
||||
|
||||
gst-launch-0.10 -v souphttpsrc location=http://gstreamer:GlytEnru@$1/videostream.cgi?rate=0 ! jpegdec ! ffmpegcolorspace ! videoscale ! ffmpegcolorspace ! $MIXERCAPS ! ximagesink
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
gst-launch-0.10 -v udpsrc port=9998 caps="application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)JPEG, payload=(int)96" ! rtpjpegdepay ! jpegdec ! videoscale ! xvimagesink
|
|
@ -1,67 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Deliver mixer1 output to screen.
|
||||
# Taken from the original snowmix sources
|
||||
|
||||
if [ -z "$1" -o -z "$2" -o -z "$3" ]; then
|
||||
echo "Usage $0 <remote ip> <width> <height>"
|
||||
exit
|
||||
fi
|
||||
|
||||
remote_ip="$1"
|
||||
width="$2"
|
||||
height="$3"
|
||||
|
||||
tmpfile=/tmp/output2screen.tmp.$$
|
||||
echo 'system info' | nc 127.0.0.1 9999 >$tmpfile
|
||||
geometry=`grep 'STAT: System geometry' $tmpfile |cut -f3 -d: |cut -f2 -d' '|tr 'x' ' '`
|
||||
framerate=`grep 'STAT: Frame rate' $tmpfile |cut -f3 -d: |cut -f2 -d' '`
|
||||
ctrsocket=`grep 'STAT: Output ctr socket' $tmpfile |cut -f3 -d: |cut -f2 -d' '`
|
||||
|
||||
rm $tmpfile
|
||||
|
||||
VIDEOCONVERT=ffmpegcolorspace
|
||||
VIDEO=video/x-raw-rgb
|
||||
AUDIO=audio/x-raw-int
|
||||
|
||||
|
||||
frameratefraction()
|
||||
{
|
||||
echo $1 | awk '{ rate=$1 ;
|
||||
factor=1;
|
||||
intrate=int(rate);
|
||||
while (factor*rate > intrate) {
|
||||
factor = factor * 10;
|
||||
intrate = int(rate*factor);
|
||||
}
|
||||
printf("%d/%d\n",intrate,factor);
|
||||
}'
|
||||
}
|
||||
ratefraction=`frameratefraction $framerate`
|
||||
|
||||
width=`echo $geometry |cut -f1 -d' '`
|
||||
height=`echo $geometry |cut -f2 -d' '`
|
||||
|
||||
MIXERFORMAT=$VIDEO', bpp=(int)32, depth=(int)32, endianness=(int)4321, format=(fourcc)BGRA, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)'$width', height=(int)'$height', framerate=(fraction)'$ratefraction', pixel-aspect-ratio=(fraction)1/1, interlaced=(boolean)false'
|
||||
OUTPUTFORMAT=$VIDEO', width='$width', height='$height
|
||||
|
||||
|
||||
while true ; do
|
||||
gst-launch-0.10 -v shmsrc socket-path=$ctrsocket do-timestamp=true is-live=true ! \
|
||||
$MIXERFORMAT ! \
|
||||
queue leaky=2 !\
|
||||
videoscale ! \
|
||||
$OUTPUTFORMAT ! \
|
||||
$VIDEOCONVERT ! \
|
||||
tee name=atee ! \
|
||||
queue ! \
|
||||
ximagesink \
|
||||
atee. !\
|
||||
queue !\
|
||||
timeoverlay !\
|
||||
ffmpegcolorspace !\
|
||||
'video/x-raw-yuv' !\
|
||||
jpegenc !\
|
||||
rtpjpegpay !\
|
||||
udpsink host=$remote_ip port=9998 sync=true
|
||||
sleep 2
|
||||
done
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
gst-launch-1.0 -v tcpclientsrc host=mixer.htt port=6666 ! tsdemux ! h264parse ! omxh264dec ! eglglessink sync=false
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
cd ~/htt-video/python/controller/
|
||||
python2 videocontroller.py localhost
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
cd /home/htt/htt-video/python/feeder/
|
||||
python2 runfeeds.py
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
|
||||
export SNOWMIX=/usr/local/lib/Snowmix-0.5.1
|
||||
cd ~/htt-video/
|
||||
snowmix ini/base.ini
|
|
@ -1,2 +1,3 @@
|
|||
#!/bin/bash
|
||||
echo quit | nc localhost 9999
|
||||
echo quit | nc localhost 9999
|
||||
rm /tmp/mixer1
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
./video2feed.sh /tmp/feed1 ../videos/LES_TDS_launch.mp4
|
|
@ -1,26 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Deliver a video file as input feed
|
||||
|
||||
if [ -z "$1" -o -z "$2" ]; then
|
||||
echo "Usage $0 <control pipe> <video file>"
|
||||
exit
|
||||
fi
|
||||
|
||||
CONTROL_PIPE="$1"
|
||||
MIXERFORMAT='video/x-raw-rgb, bpp=(int)32, depth=(int)32, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)1024, height=(int)768, framerate=(fraction)24/1, pixel-aspect-ratio=(fraction)1/1, interlaced=(boolean)false'
|
||||
SRC="filesrc location=$2 ! decodebin2 "
|
||||
SHMSIZE='shm-size=10000000'
|
||||
SHMOPTION="wait-for-connection=0"
|
||||
SHMSINK1="shmsink socket-path=$CONTROL_PIPE $SHMSIZE $SHMOPTION"
|
||||
SCALE='ffmpegcolorspace ! videoscale ! ffmpegcolorspace'
|
||||
while true ; do
|
||||
# Remove the named pipe if it exist
|
||||
rm -f $CONTROL_PIPE
|
||||
gst-launch-0.10 -v \
|
||||
$SRC ! \
|
||||
$SCALE ! \
|
||||
$MIXERFORMAT ! \
|
||||
$SHMSINK1
|
||||
sleep 2
|
||||
done
|
||||
exit
|
Loading…
Reference in New Issue