Skip to content

Commit

Permalink
Merge remote-tracking branch 'mazdermind/bugfix/137-field_order_progr…
Browse files Browse the repository at this point in the history
…essive'
  • Loading branch information
MaZderMind committed Nov 3, 2017
2 parents cfc1582 + 0d47a43 commit 5516ce8
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 47 deletions.
89 changes: 48 additions & 41 deletions voctocore/default-config.ini
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
[mix]
videocaps=video/x-raw,format=I420,width=1920,height=1080,framerate=25/1,pixel-aspect-ratio=1/1
audiocaps=audio/x-raw,format=S16LE,channels=2,layout=interleaved,rate=48000
videocaps = video/x-raw,format=I420,width=1920,height=1080,framerate=25/1,pixel-aspect-ratio=1/1,interlace-mode=progressive
audiocaps = audio/x-raw,format=S16LE,channels=2,layout=interleaved,rate=48000

; tcp-ports will be 10000,10001,10002
sources=cam1,cam2,grabber
sources = cam1,cam2,grabber

; set the initial audio source (shortcut for setting the volume of the
; audio-sources to 1.0), defaults to the first source
;audiosource=cam1
;audiosource = cam1

;[source.cam1]
;kind=decklink
;devicenumber=0
;video_connection=SDI
;video_mode=1080i50
;audio_connection=embedded
;kind = decklink
;devicenumber = 0
;video_connection = SDI
;video_mode = 1080i50
;audio_connection = embedded
;deinterlace = yes
;deinterlace = no
;volume=0.5

;[source.cam2]
;volume=0.5
;kind = tcp
;deinterlace = yes
;deinterlace = no
;deinterlace = assume-progressive
;volume = 0.5

;[source.background]
;kind=img
;imguri=file:///opt/voc/share/background.png
;kind = img
;imguri = file:///opt/voc/share/background.png


[output-buffers]
Expand All @@ -35,68 +42,68 @@ sources=cam1,cam2,grabber
; you might want to up that even more for your recording-sink, so that it never
; gets disconnected. for this reason, the following configuration raises the
; default limit for the mix_out sink to a whopping 10'000 frames (400 seconds)
;cam1_mirror=500
;cam2_mirror=500
;grabber_mirror=500
mix_out=10000
;streamblanker_out=500
;cam1_mirror = 500
;cam2_mirror = 500
;grabber_mirror = 500
mix_out = 10000
;streamblanker_out = 500

[fullscreen]
; if configured, switching to fullscreen will automatically select this
; source. if not configured, it will not change the last set source
;default-a=cam1
;default-a = cam1

[side-by-side-equal]
; defaults to 1% of the video width
;gutter=12
;atop=50
;btop=200
;gutter = 12
;atop = 50
;btop = 200

; if configured, switching to the sbs-equal mode will automatically select these
; sources. if not configured, it will not change the last set sources
;default-a=cam1
;default-b=cam2
;default-a = cam1
;default-b = cam2

[side-by-side-preview]
;asize=1024x576
;apos=12/12
;bsize=320x180
;bpos=948/528
;asize = 1024x576
;apos = 12/12
;bsize = 320x180
;bpos = 948/528

; automatically select these sources when switching to sbs-preview
;default-a=grabber
;default-b=cam1
;default-a = grabber
;default-b = cam1

[picture-in-picture]
;pipsize=320x180
;pippos=948/528
;pipsize = 320x180
;pippos = 948/528

; automatically select these sources when switching to pip
;default-a=grabber
;default-b=cam1
;default-a = grabber
;default-b = cam1

[previews]
; disable if ui & server run on the same computer and can exchange uncompressed video frames
enabled=false
deinterlace=false
enabled = false
deinterlace = false

; use vaapi to encode the previews, can be h264, mpeg2 or jpeg (BUT ONLY h264 IS TESTED)
; not all encoders are available on all CPUs
;vaapi=h264
;vaapi = h264

; default to mix-videocaps, only applicable if enabled=true
; you can change the framerate and the width/height, but nothing else
;videocaps=video/x-raw,width=1024,height=576,framerate=25/1
;videocaps = video/x-raw,width=1024,height=576,framerate=25/1

[stream-blanker]
enabled=true
sources=pause,nostream
volume=1.0
enabled = true
sources = pause,nostream
volume = 1.0

;[source.stream-blanker-pause]
;kind=img
;imguri=file:///home/peter/VOC/voctomix/bgloop.jpg

[mirrors]
; disable if not needed
enabled=true
enabled = true
21 changes: 20 additions & 1 deletion voctocore/lib/sources/avsource.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@


class AVSource(object, metaclass=ABCMeta):

def __init__(self, name, outputs=None, has_audio=True, has_video=True):
if not self.log:
self.log = logging.getLogger('AVSource[{}]'.format(name))
Expand Down Expand Up @@ -55,6 +54,7 @@ def build_pipeline(self, pipeline, aelem=None, velem=None):
tee name=vtee
""".format(
velem=velem,
deinterlacer=self.build_deinterlacer(),
vcaps=Config.get('mix', 'videocaps')
)

Expand All @@ -74,6 +74,25 @@ def build_pipeline(self, pipeline, aelem=None, velem=None):
self.pipeline.bus.connect("message::eos", self.on_eos)
self.pipeline.bus.connect("message::error", self.on_error)

def build_deinterlacer(self):
deinterlace_config = self.get_deinterlace_config()

if deinterlace_config == "yes":
return "yadif mode=interlaced"

elif deinterlace_config == "no":
return ""

else:
raise RuntimeError(
"Unknown Deinterlace-Mode on source {} configured: {}"
.format(self.name, deinterlace_config))

def get_deinterlace_config(self):
section = 'source.{}'.format(self.name)
deinterlace_config = Config.get(section, 'deinterlace', fallback="no")
return deinterlace_config

def on_eos(self, bus, message):
self.log.debug('Received End-of-Stream-Signal on Source-Pipeline')

Expand Down
18 changes: 15 additions & 3 deletions voctocore/lib/sources/decklinkavsource.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@


class DeckLinkAVSource(AVSource):

def __init__(self, name, outputs=None, has_audio=True, has_video=True):
self.log = logging.getLogger('DecklinkAVSource[{}]'.format(name))
super().__init__(name, outputs, has_audio, has_video)

section = 'source.{}'.format(name)

# Device number, default: 0
self.device = Config.get(section, 'devicenumber', fallback=0)

# Audio connection, default: Automatic
self.aconn = Config.get(section, 'audio_connection', fallback='auto')

# Video connection, default: Automatic
self.vconn = Config.get(section, 'video_connection', fallback='auto')

# Video mode, default: 1080i50
self.vmode = Config.get(section, 'video_mode', fallback='1080i50')

Expand Down Expand Up @@ -45,10 +48,12 @@ def launch_pipeline(self):
if self.has_video:
pipeline += """
videoconvert !
yadif !
{deinterlacer}
videoscale !
videorate name=deckvideo
"""
""".format(
deinterlacer=self.build_deinterlacer()
)
else:
pipeline += """
fakesink
Expand All @@ -67,6 +72,13 @@ def launch_pipeline(self):
self.build_pipeline(pipeline, aelem='deckaudio', velem='deckvideo')
self.pipeline.set_state(Gst.State.PLAYING)

def build_deinterlacer(self):
deinterlacer = super().build_deinterlacer()
if deinterlacer != '':
deinterlacer += ' !'

return deinterlacer

def restart(self):
self.pipeline.set_state(Gst.State.NULL)
self.launch_pipeline()
48 changes: 46 additions & 2 deletions voctocore/lib/sources/tcpavsource.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@


class TCPAVSource(AVSource, TCPSingleConnection):

def __init__(self, name, port, outputs=None,
has_audio=True, has_video=True):
self.log = logging.getLogger('TCPAVSource[{}]'.format(name))
Expand All @@ -24,14 +23,27 @@ def __str__(self):
)

def on_accepted(self, conn, addr):
deinterlacer = self.build_deinterlacer()
pipeline = """
fdsrc fd={fd} blocksize=1048576 !
queue !
matroskademux name=demux
""".format(
fd=conn.fileno()
)
self.build_pipeline(pipeline, aelem='demux', velem='demux')

if deinterlacer:
pipeline += """
demux. !
video/x-raw !
{deinterlacer}
""".format(
deinterlacer=self.build_deinterlacer()
)
self.build_pipeline(pipeline, aelem='demux', velem='deinter')

else:
self.build_pipeline(pipeline, aelem='demux', velem='demux')

self.audio_caps = Gst.Caps.from_string(Config.get('mix', 'audiocaps'))
self.video_caps = Gst.Caps.from_string(Config.get('mix', 'videocaps'))
Expand All @@ -41,6 +53,20 @@ def on_accepted(self, conn, addr):

self.pipeline.set_state(Gst.State.PLAYING)

def build_deinterlacer(self):
deinterlace_config = self.get_deinterlace_config()

if deinterlace_config == "assume-progressive":
deinterlacer = "capssetter " \
"caps=video/x-raw,interlace-mode=progressive"
else:
deinterlacer = super().build_deinterlacer()

if deinterlacer != '':
deinterlacer += ' name=deinter'

return deinterlacer

def on_pad_added(self, demux, src_pad):
caps = src_pad.query_caps(None)
self.log.debug('demuxer added pad w/ caps: %s', caps.to_string())
Expand All @@ -66,6 +92,8 @@ def on_pad_added(self, demux, src_pad):
self.log.warning(' configured caps: %s',
self.video_caps.to_string())

self.test_and_warn_interlace_mode(caps)

def on_eos(self, bus, message):
super().on_eos(bus, message)
if self.currentConnection is not None:
Expand All @@ -84,3 +112,19 @@ def disconnect(self):
def restart(self):
if self.currentConnection is not None:
self.disconnect()

def test_and_warn_interlace_mode(self, caps):
interlace_mode = caps.get_structure(0).get_string('interlace-mode')
deinterlace_config = self.get_deinterlace_config()

if interlace_mode == 'mixed' and deinterlace_config == 'no':
self.log.warning(
'your source sent an interlace_mode-flag in the matroska-'
'container, specifying the source-video-stream is of '
'mixed-mode.\n'
'this is probably a gstreamer-bug which is triggered with '
'recent ffmpeg-versions\n'
'setting [source.{name}] deinterlace=assume-progressive '
'might help see https://github.com/voc/voctomix/issues/137 '
'for more information'.format(name=self.name)
)
3 changes: 3 additions & 0 deletions voctocore/tests/helper/voctomix_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import socket
import unittest

import gi.repository
Expand All @@ -11,6 +12,8 @@
gi.repository.GObject = MagicMock()
lib.config.Config = ConfigMock.WithBasicConfig()

socket.socket = MagicMock()


class VoctomixTest(unittest.TestCase):
"""Base-Class for all Voctomix-Tests"""
Expand Down
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from tests.helper.voctomix_test import VoctomixTest
from gi.repository import Gst
from lib.sources import DeckLinkAVSource
from lib.config import Config


# noinspection PyUnusedLocal
class AudiomixMultipleSources(VoctomixTest):
def setUp(self):
super().setUp()

Config.given("mix", "videocaps", "video/x-raw")
Config.given("source.cam1", "kind", "decklink")
Config.given("source.cam1", "devicenumber", "23")

self.source = DeckLinkAVSource('cam1', ['test_mixer', 'test_preview'], has_audio=True, has_video=True)

def test_unconfigured_does_not_add_a_deinterlacer(self):
pipeline = self.simulate_connection_and_aquire_pipeline_description()
self.assertRegexAnyWhitespace(pipeline, "videoconvert ! videoscale")

def test_no_does_not_add_a_deinterlacer(self):
Config.given("source.cam1", "deinterlace", "no")

pipeline = self.simulate_connection_and_aquire_pipeline_description()
self.assertRegexAnyWhitespace(pipeline, "videoconvert ! videoscale")

def test_yes_does_add_yadif(self):
Config.given("source.cam1", "deinterlace", "yes")

pipeline = self.simulate_connection_and_aquire_pipeline_description()
self.assertRegexAnyWhitespace(pipeline, "videoconvert ! yadif mode=interlaced ! videoscale")

def simulate_connection_and_aquire_pipeline_description(self):
Gst.parse_launch.reset_mock()
self.source.launch_pipeline()

Gst.parse_launch.assert_called()
args, kwargs = Gst.parse_launch.call_args_list[0]
pipeline = args[0]

return pipeline

def assertRegexAnyWhitespace(self, text, regex):
regex = regex.replace(" ", "\s*")
self.assertRegex(text, regex)
Empty file.
Loading

0 comments on commit 5516ce8

Please sign in to comment.