Skip to contentSkip to conversation

Media Device Allocator API

PATCH Superseded Shuah Khan 3 participants 18 messages Jan 24, 2019 – Feb 6, 2019

Subsystems (4)

v10 of 15 Superseded Latest is v15 →

lore
Share & tools
lore

Shuah Khan

author
#0 lore raw

v10 cover Media Device Allocator API

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

- This patch series is tested on 5.0-rc3 and addresses comments on v9 series from Hans Verkuil.
- v9 was tested on 4.20-rc6.
- Tested sharing resources with kaffeine, vlc, xawtv, tvtime, and arecord. When analog is streaming, digital and audio user-space applications detect that the tuner is busy and exit. When digital is streaming, analog and audio applications detect that the tuner is busy and exit. When arecord is owns the tuner, digital and analog detect that the tuner is busy and exit.
- Tested media device allocator API with bind/unbind testing on snd-usb-audio and au0828 drivers to make sure /dev/mediaX is released only when the last driver is unbound.
- Addressed review comments from Hans on the RFC v8 (rebased on 4.19)
- Updated change log to describe the use-case more clearly.
- No changes to 0001,0002 code since the v7 referenced below.
- 0003 is a new patch to enable ALSA defines that have been disabled for kernel between 4.9 and 4.19.
- Minor merge conflict resolution in 0004.
- Added SPDX to new files.

Changes since v9:
- Patch 1: Fix mutex assert warning from find_module() calls. This code was written before the change to find_module() that requires callers to hold module_mutex. I missed this during my testing on 4.20-rc6. Hans Verkuil reported the problem.
- Patch 4: sound/usb: Initializes all the entities it can before registering the device based on comments from Hans Verkuil
- Carried Reviewed-by tag from Takashi Iwai for the sound from v9.
- No changes to Patches 2 and 3.

References:
https://lkml.org/lkml/2018/11/2/169
https://www.mail-archive.com/[email protected]/msg105854.html

Shuah Khan (4):
media: Media Device Allocator API
media: change au0828 to use Media Device Allocator API
media: media.h: Enable ALSA MEDIA_INTF_T* interface types sound/usb: Use Media Controller API to share media resources

 Documentation/media/kapi/mc-core.rst   |  41 ++++
 drivers/media/Makefile                 |   4 +
 drivers/media/media-dev-allocator.c    | 144 +++++++++++
 drivers/media/usb/au0828/au0828-core.c |  12 +-
 drivers/media/usb/au0828/au0828.h      |   1 +
 include/media/media-dev-allocator.h    |  53 ++++
 include/uapi/linux/media.h             |  25 +-
 sound/usb/Kconfig                      |   4 +
 sound/usb/Makefile                     |   2 +
 sound/usb/card.c                       |  14 ++
 sound/usb/card.h                       |   3 +
 sound/usb/media.c                      | 327 +++++++++++++++++++++++++
 sound/usb/media.h                      |  74 ++++++
 sound/usb/mixer.h                      |   3 +
 sound/usb/pcm.c                        |  29 ++-
 sound/usb/quirks-table.h               |   1 +
 sound/usb/stream.c                     |   2 +
 sound/usb/usbaudio.h                   |   6 +
 18 files changed, 723 insertions(+), 22 deletions(-)
 create mode 100644 drivers/media/media-dev-allocator.c
 create mode 100644 include/media/media-dev-allocator.h
 create mode 100644 sound/usb/media.c
 create mode 100644 sound/usb/media.h
Signature · 1 line
2.17.1
author
#1 lore raw

PATCH v10 1/4 media: Media Device Allocator API

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

---
 Documentation/media/kapi/mc-core.rst |  41 ++++++++
 drivers/media/Makefile               |   4 +
 drivers/media/media-dev-allocator.c  | 144 +++++++++++++++++++++++++++
 include/media/media-dev-allocator.h  |  53 ++++++++++
 4 files changed, 242 insertions(+)
 create mode 100644 drivers/media/media-dev-allocator.c
 create mode 100644 include/media/media-dev-allocator.h
Documentation/media/kapi/mc-core.rst +41 -0
index 0bcfeadbc52d..07f2a6a90af2 100644
@@ -259,6 +259,45 @@ Subsystems should facilitate link validation by providing subsystem specific
 helper functions to provide easy access for commonly needed information, and
 in the end provide a way to use driver-specific callbacks.
 
+Media Controller Device Allocator API
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When the media device belongs to more than one driver, the shared media
+device is allocated with the shared struct device as the key for look ups.
+
+The shared media device should stay in registered state until the last
+driver unregisters it. In addition, the media device should be released when
+all the references are released. Each driver gets a reference to the media
+device during probe, when it allocates the media device. If media device is
+already allocated, the allocate API bumps up the refcount and returns the
+existing media device. The driver puts the reference back in its disconnect
+routine when it calls :c:func:`media_device_delete()`.
+
+The media device is unregistered and cleaned up from the kref put handler to
+ensure that the media device stays in registered state until the last driver
+unregisters the media device.
+
+**Driver Usage**
+
+Drivers should use the appropriate media-core routines to manage the shared
+media device life-time handling the two states:
+1. allocate -> register -> delete
+2. get reference to already registered device -> delete
+
+call :c:func:`media_device_delete()` routine to make sure the shared media
+device delete is handled correctly.
+
+**driver probe:**
+Call :c:func:`media_device_usb_allocate()` to allocate or get a reference
+Call :c:func:`media_device_register()`, if media devnode isn't registered
+
+**driver disconnect:**
+Call :c:func:`media_device_delete()` to free the media_device. Freeing is
+handled by the kref put handler.
+
+API Definitions
+^^^^^^^^^^^^^^^
+
 .. kernel-doc:: include/media/media-device.h
 
 .. kernel-doc:: include/media/media-devnode.h
@@ -266,3 +305,5 @@ in the end provide a way to use driver-specific callbacks.
 .. kernel-doc:: include/media/media-entity.h
 
 .. kernel-doc:: include/media/media-request.h
+
+.. kernel-doc:: include/media/media-dev-allocator.h
drivers/media/Makefile +4 -0
index 985d35ec6b29..1d7653318af6 100644
@@ -6,6 +6,10 @@
 media-objs	:= media-device.o media-devnode.o media-entity.o \
 		   media-request.o
 
+ifeq ($(CONFIG_USB),y)
+	media-objs += media-dev-allocator.o
+endif
+
 #
 # I2C drivers should come before other drivers, otherwise they'll fail
 # when compiled as builtin drivers
drivers/media/media-dev-allocator.c new file +144 -0

144 lines not on this page · raw message

include/media/media-dev-allocator.h new file +53 -0

53 lines not on this page · raw message

Signature · 1 line
2.17.1
#2 lore raw

Re: [PATCH v10 1/4] media: Media Device Allocator API

Hi Shuah,

Quoted 11 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Are there real, non-USB devices that could use the same API?

Quoted 88 lines
---
 Documentation/media/kapi/mc-core.rst |  41 ++++++++
 drivers/media/Makefile               |   4 +
 drivers/media/media-dev-allocator.c  | 144 +++++++++++++++++++++++++++
 include/media/media-dev-allocator.h  |  53 ++++++++++
 4 files changed, 242 insertions(+)
 create mode 100644 drivers/media/media-dev-allocator.c
 create mode 100644 include/media/media-dev-allocator.h
Documentation/media/kapi/mc-core.rst +41 -0
index 0bcfeadbc52d..07f2a6a90af2 100644
@@ -259,6 +259,45 @@ Subsystems should facilitate link validation by providing subsystem specific
 helper functions to provide easy access for commonly needed information, and
 in the end provide a way to use driver-specific callbacks.
 
+Media Controller Device Allocator API
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When the media device belongs to more than one driver, the shared media
+device is allocated with the shared struct device as the key for look ups.
+
+The shared media device should stay in registered state until the last
+driver unregisters it. In addition, the media device should be released when
+all the references are released. Each driver gets a reference to the media
+device during probe, when it allocates the media device. If media device is
+already allocated, the allocate API bumps up the refcount and returns the
+existing media device. The driver puts the reference back in its disconnect
+routine when it calls :c:func:`media_device_delete()`.
+
+The media device is unregistered and cleaned up from the kref put handler to
+ensure that the media device stays in registered state until the last driver
+unregisters the media device.
+
+**Driver Usage**
+
+Drivers should use the appropriate media-core routines to manage the shared
+media device life-time handling the two states:
+1. allocate -> register -> delete
+2. get reference to already registered device -> delete
+
+call :c:func:`media_device_delete()` routine to make sure the shared media
+device delete is handled correctly.
+
+**driver probe:**
+Call :c:func:`media_device_usb_allocate()` to allocate or get a reference
+Call :c:func:`media_device_register()`, if media devnode isn't registered
+
+**driver disconnect:**
+Call :c:func:`media_device_delete()` to free the media_device. Freeing is
+handled by the kref put handler.
+
+API Definitions
+^^^^^^^^^^^^^^^
+
 .. kernel-doc:: include/media/media-device.h
 
 .. kernel-doc:: include/media/media-devnode.h
@@ -266,3 +305,5 @@ in the end provide a way to use driver-specific callbacks.
 .. kernel-doc:: include/media/media-entity.h
 
 .. kernel-doc:: include/media/media-request.h
+
+.. kernel-doc:: include/media/media-dev-allocator.h
drivers/media/Makefile +4 -0
index 985d35ec6b29..1d7653318af6 100644
@@ -6,6 +6,10 @@
 media-objs	:= media-device.o media-devnode.o media-entity.o \
 		   media-request.o
 
+ifeq ($(CONFIG_USB),y)
+	media-objs += media-dev-allocator.o
+endif
+
 #
 # I2C drivers should come before other drivers, otherwise they'll fail
 # when compiled as builtin drivers
drivers/media/media-dev-allocator.c new file +0 -0
new file mode 100644
index 000000000000..4606456c1e86

drivers/media/media-dev-allocator.c line 1

Quoted 1 line
drivers/media/media-dev-allocator.c new file +1 -0
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-or-later

GPL-2.0+

Quoted 40 lines
drivers/media/media-dev-allocator.c new file +40 -0
+/*
+ * media-dev-allocator.c - Media Controller Device Allocator API
+ *
+ * Copyright (c) 2018 Shuah Khan <[email protected]>
+ *
+ * Credits: Suggested by Laurent Pinchart <[email protected]>
+ */
+
+/*
+ * This file adds a global refcounted Media Controller Device Instance API.
+ * A system wide global media device list is managed and each media device
+ * includes a kref count. The last put on the media device releases the media
+ * device instance.
+ *
+ */
+
+#include <linux/kref.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include <media/media-device.h>
+
+static LIST_HEAD(media_device_list);
+static DEFINE_MUTEX(media_device_lock);
+
+struct media_device_instance {
+	struct media_device mdev;
+	struct module *owner;
+	struct list_head list;
+	struct kref refcount;
+};
+
+static inline struct media_device_instance *
+to_media_device_instance(struct media_device *mdev)
+{
+	return container_of(mdev, struct media_device_instance, mdev);
+}
+
+static void media_device_instance_release(struct kref *kref)

drivers/media/media-dev-allocator.c line 51

Quoted 10 lines
drivers/media/media-dev-allocator.c new file +10 -0
+{
+	struct media_device_instance *mdi =
+		container_of(kref, struct media_device_instance, refcount);
+
+	dev_dbg(mdi->mdev.dev, "%s: mdev=%p\n", __func__, &mdi->mdev);
+
+	mutex_lock(&media_device_lock);
+
+	media_device_unregister(&mdi->mdev);
+	media_device_cleanup(&mdi->mdev);

This is a problem, albeit not really more of a problem than it is in a driver. The refcounting changes can be made here instead. I'll take this into account in the media device refcounting series I'm planning to start working on again; would you be perhaps able to help testing with this device once I have patches in that shape? I have no access to the hardware.

Quoted 33 lines
drivers/media/media-dev-allocator.c new file +33 -0
+
+	list_del(&mdi->list);
+	mutex_unlock(&media_device_lock);
+
+	kfree(mdi);
+}
+
+/* Callers should hold media_device_lock when calling this function */
+static struct media_device *__media_device_get(struct device *dev,
+						const char *module_name,
+						struct module *modp)
+{
+	struct media_device_instance *mdi;
+
+	list_for_each_entry(mdi, &media_device_list, list) {
+
+		if (mdi->mdev.dev != dev)
+			continue;
+
+		kref_get(&mdi->refcount);
+
+		/* get module reference for the media_device owner */
+		if (modp != mdi->owner && !try_module_get(mdi->owner))
+			dev_err(dev, "%s: try_module_get() error\n", __func__);
+		dev_dbg(dev, "%s: get mdev=%p module_name %s\n",
+			__func__, &mdi->mdev, module_name);
+		return &mdi->mdev;
+	}
+
+	mdi = kzalloc(sizeof(*mdi), GFP_KERNEL);
+	if (!mdi)
+		return NULL;
+

drivers/media/media-dev-allocator.c line 94

Quoted 10 lines
drivers/media/media-dev-allocator.c new file +10 -0
+	mdi->owner = modp;
+	kref_init(&mdi->refcount);
+	list_add_tail(&mdi->list, &media_device_list);
+
+	dev_dbg(dev, "%s: alloc mdev=%p module_name %s\n", __func__,
+		&mdi->mdev, module_name);
+	return &mdi->mdev;
+}
+
+#if IS_ENABLED(CONFIG_USB)

You already compile the file only if CONFIG_USB is enabled. I think you could remove this.

drivers/media/media-dev-allocator.c line 96

Quoted 2 lines
drivers/media/media-dev-allocator.c new file +2 -0
+struct media_device *media_device_usb_allocate(struct usb_device *udev,
+					       const char *module_name)

I'd like to suggest working based on usb_interface instead of usb_device
here: that object already exists and you can find out the device based on it. It seems all callers of this function already have the usb_interface around.

Quoted 16 lines
drivers/media/media-dev-allocator.c new file +16 -0
+{
+	struct media_device *mdev;
+	struct module *modptr;
+
+	mutex_lock(&module_mutex);
+	modptr = find_module(module_name);
+	mutex_unlock(&module_mutex);
+
+	mutex_lock(&media_device_lock);
+	mdev = __media_device_get(&udev->dev, module_name, modptr);
+	if (!mdev) {
+		mutex_unlock(&media_device_lock);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	/* check if media device is already initialized */

drivers/media/media-dev-allocator.c line 122

Quoted 10 lines
drivers/media/media-dev-allocator.c new file +10 -0
+	if (!mdev->dev)
+		__media_device_usb_init(mdev, udev, udev->product,
+					module_name);
+	mutex_unlock(&media_device_lock);
+	return mdev;
+}
+EXPORT_SYMBOL_GPL(media_device_usb_allocate);
+#endif
+
+void media_device_delete(struct media_device *mdev, const char *module_name)

Same here. The use of the module name seems a bit hackish to me, albeit I suppose it'd work, too.

Quoted 81 lines
drivers/media/media-dev-allocator.c new file +22 -0
+{
+	struct media_device_instance *mdi = to_media_device_instance(mdev);
+	struct module *modptr;
+
+	dev_dbg(mdi->mdev.dev, "%s: mdev=%p module_name %s\n",
+		__func__, &mdi->mdev, module_name);
+
+	mutex_lock(&module_mutex);
+	modptr = find_module(module_name);
+	mutex_unlock(&module_mutex);
+
+	mutex_lock(&media_device_lock);
+	/* put module reference if media_device owner is not THIS_MODULE */
+	if (mdi->owner != modptr) {
+		module_put(mdi->owner);
+		dev_dbg(mdi->mdev.dev,
+			"%s decremented owner module reference\n", __func__);
+	}
+	mutex_unlock(&media_device_lock);
+	kref_put(&mdi->refcount, media_device_instance_release);
+}
+EXPORT_SYMBOL_GPL(media_device_delete);
include/media/media-dev-allocator.h new file +53 -0
new file mode 100644
index 000000000000..9164795e911c
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * media-dev-allocator.h - Media Controller Device Allocator API
+ *
+ * Copyright (c) 2018 Shuah Khan <[email protected]>
+ *
+ * Credits: Suggested by Laurent Pinchart <[email protected]>
+ */
+
+/*
+ * This file adds a global ref-counted Media Controller Device Instance API.
+ * A system wide global media device list is managed and each media device
+ * includes a kref count. The last put on the media device releases the media
+ * device instance.
+ */
+
+#ifndef _MEDIA_DEV_ALLOCTOR_H
+#define _MEDIA_DEV_ALLOCTOR_H
+
+struct usb_device;
+
+#if defined(CONFIG_MEDIA_CONTROLLER) && defined(CONFIG_USB)
+/**
+ * media_device_usb_allocate() - Allocate and return struct &media device
+ *
+ * @udev:		struct &usb_device pointer
+ * @module_name:	should be filled with %KBUILD_MODNAME
+ *
+ * This interface should be called to allocate a Media Device when multiple
+ * drivers share usb_device and the media device. This interface allocates
+ * &media_device structure and calls media_device_usb_init() to initialize
+ * it.
+ *
+ */
+struct media_device *media_device_usb_allocate(struct usb_device *udev,
+					       char *module_name);
+/**
+ * media_device_delete() - Release media device. Calls kref_put().
+ *
+ * @mdev:		struct &media_device pointer
+ * @module_name:	should be filled with %KBUILD_MODNAME
+ *
+ * This interface should be called to put Media Device Instance kref.
+ */
+void media_device_delete(struct media_device *mdev, char *module_name);
+#else
+static inline struct media_device *media_device_usb_allocate(
+			struct usb_device *udev, char *module_name)
+			{ return NULL; }
+static inline void media_device_delete(
+			struct media_device *mdev, char *module_name) { }
+#endif /* CONFIG_MEDIA_CONTROLLER */
+#endif
Signature · 3 lines
Regards,

Sakari Ailus
author
#3 lore raw

Re: [PATCH v10 1/4] media: Media Device Allocator API

Hi Sakari,

Quoted 10 lines · Sakari Ailus

Hi Shuah,

Quoted 7 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Quoted 7 lines
Quoted 4 lines

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Are there real, non-USB devices that could use the same API?

There might be. I don't have any to test. This patch is restricted to USB at the moment.

Quoted 75 lines
Quoted 75 lines
---
  Documentation/media/kapi/mc-core.rst |  41 ++++++++
  drivers/media/Makefile               |   4 +
  drivers/media/media-dev-allocator.c  | 144 +++++++++++++++++++++++++++
  include/media/media-dev-allocator.h  |  53 ++++++++++
  4 files changed, 242 insertions(+)

create mode 100644 drivers/media/media-dev-allocator.c create mode 100644 include/media/media-dev-allocator.h

Documentation/media/kapi/mc-core.rst +41 -0
index 0bcfeadbc52d..07f2a6a90af2 100644
@@ -259,6 +259,45 @@ Subsystems should facilitate link validation by providing subsystem specific
  helper functions to provide easy access for commonly needed information, and
  in the end provide a way to use driver-specific callbacks.
 
+Media Controller Device Allocator API
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When the media device belongs to more than one driver, the shared media
+device is allocated with the shared struct device as the key for look ups.
+
+The shared media device should stay in registered state until the last
+driver unregisters it. In addition, the media device should be released when
+all the references are released. Each driver gets a reference to the media
+device during probe, when it allocates the media device. If media device is
+already allocated, the allocate API bumps up the refcount and returns the
+existing media device. The driver puts the reference back in its disconnect
+routine when it calls :c:func:`media_device_delete()`.
+
+The media device is unregistered and cleaned up from the kref put handler to
+ensure that the media device stays in registered state until the last driver
+unregisters the media device.
+
+**Driver Usage**
+
+Drivers should use the appropriate media-core routines to manage the shared
+media device life-time handling the two states:
+1. allocate -> register -> delete
+2. get reference to already registered device -> delete
+
+call :c:func:`media_device_delete()` routine to make sure the shared media
+device delete is handled correctly.
+
+**driver probe:**
+Call :c:func:`media_device_usb_allocate()` to allocate or get a reference
+Call :c:func:`media_device_register()`, if media devnode isn't registered
+
+**driver disconnect:**
+Call :c:func:`media_device_delete()` to free the media_device. Freeing is
+handled by the kref put handler.
+
+API Definitions
+^^^^^^^^^^^^^^^
+
  .. kernel-doc:: include/media/media-device.h
 
  .. kernel-doc:: include/media/media-devnode.h
@@ -266,3 +305,5 @@ in the end provide a way to use driver-specific callbacks.
  .. kernel-doc:: include/media/media-entity.h
 
  .. kernel-doc:: include/media/media-request.h
+
+.. kernel-doc:: include/media/media-dev-allocator.h
drivers/media/Makefile +0 -0
index 985d35ec6b29..1d7653318af6 100644
@@ -6,6 +6,10 @@
  media-objs	:= media-device.o media-devnode.o media-entity.o \
  		   media-request.o
 
Quoted 17 lines
Quoted 13 lines
drivers/media/Makefile +4 -0
+ifeq ($(CONFIG_USB),y)
+	media-objs += media-dev-allocator.o
+endif
+
  #
  # I2C drivers should come before other drivers, otherwise they'll fail
  # when compiled as builtin drivers
drivers/media/media-dev-allocator.c new file +0 -0
new file mode 100644
index 000000000000..4606456c1e86

drivers/media/media-dev-allocator.c line 1

Quoted 1 line
drivers/media/media-dev-allocator.c new file +1 -0
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-or-later

GPL-2.0+

I would like to address this in a follow-on patch instead of re-doing the series again. Hope that is okay.

Quoted 45 lines
Quoted 45 lines
drivers/media/media-dev-allocator.c new file +45 -0
+/*
+ * media-dev-allocator.c - Media Controller Device Allocator API
+ *
+ * Copyright (c) 2018 Shuah Khan <[email protected]>
+ *
+ * Credits: Suggested by Laurent Pinchart <[email protected]>
+ */
+
+/*
+ * This file adds a global refcounted Media Controller Device Instance API.
+ * A system wide global media device list is managed and each media device
+ * includes a kref count. The last put on the media device releases the media
+ * device instance.
+ *
+ */
+
+#include <linux/kref.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include <media/media-device.h>
+
+static LIST_HEAD(media_device_list);
+static DEFINE_MUTEX(media_device_lock);
+
+struct media_device_instance {
+	struct media_device mdev;
+	struct module *owner;
+	struct list_head list;
+	struct kref refcount;
+};
+
+static inline struct media_device_instance *
+to_media_device_instance(struct media_device *mdev)
+{
+	return container_of(mdev, struct media_device_instance, mdev);
+}
+
+static void media_device_instance_release(struct kref *kref)
+{
+	struct media_device_instance *mdi =
+		container_of(kref, struct media_device_instance, refcount);
+
+	dev_dbg(mdi->mdev.dev, "%s: mdev=%p\n", __func__, &mdi->mdev);
Quoted 8 lines

drivers/media/media-dev-allocator.c line 51

Quoted 5 lines
drivers/media/media-dev-allocator.c new file +5 -0
+
+	mutex_lock(&media_device_lock);
+
+	media_device_unregister(&mdi->mdev);
+	media_device_cleanup(&mdi->mdev);

This is a problem, albeit not really more of a problem than it is in a driver.

Okay good to know that it can be addressed in your media device refcounting series.

The refcounting changes can be made here instead. I'll take this

Quoted 4 lines

into account in the media device refcounting series I'm planning to start working on again; would you be perhaps able to help testing with this device once I have patches in that shape? I have no access to the hardware.

Absolutely. I will be happy to help you with testing on the same hardware, I used for this series.

Quoted 47 lines
Quoted 33 lines
drivers/media/media-dev-allocator.c new file +33 -0
+
+	list_del(&mdi->list);
+	mutex_unlock(&media_device_lock);
+
+	kfree(mdi);
+}
+
+/* Callers should hold media_device_lock when calling this function */
+static struct media_device *__media_device_get(struct device *dev,
+						const char *module_name,
+						struct module *modp)
+{
+	struct media_device_instance *mdi;
+
+	list_for_each_entry(mdi, &media_device_list, list) {
+
+		if (mdi->mdev.dev != dev)
+			continue;
+
+		kref_get(&mdi->refcount);
+
+		/* get module reference for the media_device owner */
+		if (modp != mdi->owner && !try_module_get(mdi->owner))
+			dev_err(dev, "%s: try_module_get() error\n", __func__);
+		dev_dbg(dev, "%s: get mdev=%p module_name %s\n",
+			__func__, &mdi->mdev, module_name);
+		return &mdi->mdev;
+	}
+
+	mdi = kzalloc(sizeof(*mdi), GFP_KERNEL);
+	if (!mdi)
+		return NULL;
+

drivers/media/media-dev-allocator.c line 94

Quoted 10 lines
drivers/media/media-dev-allocator.c new file +10 -0
+	mdi->owner = modp;
+	kref_init(&mdi->refcount);
+	list_add_tail(&mdi->list, &media_device_list);
+
+	dev_dbg(dev, "%s: alloc mdev=%p module_name %s\n", __func__,
+		&mdi->mdev, module_name);
+	return &mdi->mdev;
+}
+
+#if IS_ENABLED(CONFIG_USB)

You already compile the file only if CONFIG_USB is enabled. I think you could remove this.

Quoted 8 lines

drivers/media/media-dev-allocator.c line 96

Quoted 2 lines
drivers/media/media-dev-allocator.c new file +2 -0
+struct media_device *media_device_usb_allocate(struct usb_device *udev,
+					       const char *module_name)

I'd like to suggest working based on usb_interface instead of usb_device
here: that object already exists and you can find out the device based on it. It seems all callers of this function already have the usb_interface around.

Is there an advantage to using interface instead of the parent device? Is there a problem doing it this way?

Quoted 21 lines
Quoted 21 lines
drivers/media/media-dev-allocator.c new file +21 -0
+{
+	struct media_device *mdev;
+	struct module *modptr;
+
+	mutex_lock(&module_mutex);
+	modptr = find_module(module_name);
+	mutex_unlock(&module_mutex);
+
+	mutex_lock(&media_device_lock);
+	mdev = __media_device_get(&udev->dev, module_name, modptr);
+	if (!mdev) {
+		mutex_unlock(&media_device_lock);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	/* check if media device is already initialized */
+	if (!mdev->dev)
+		__media_device_usb_init(mdev, udev, udev->product,
+					module_name);
+	mutex_unlock(&media_device_lock);
+	return mdev;
Quoted 9 lines

drivers/media/media-dev-allocator.c line 122

Quoted 5 lines
drivers/media/media-dev-allocator.c new file +5 -0
+}
+EXPORT_SYMBOL_GPL(media_device_usb_allocate);
+#endif
+
+void media_device_delete(struct media_device *mdev, const char *module_name)

Same here. The use of the module name seems a bit hackish to me, albeit I suppose it'd work, too.

What is hackish about it? I found it very useful to use it in debug and error messages that are user-friendly.

thanks,
-- Shuah

author
#4 lore raw

PATCH v10 2/4 media: change au0828 to use Media Device Allocator API

In reply to Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Change au0828 to use Media Device Allocator API to allocate media device with the parent usb struct device as the key, so it can be shared with the snd_usb_audio driver.

---
 drivers/media/usb/au0828/au0828-core.c | 12 ++++--------
 drivers/media/usb/au0828/au0828.h      |  1 +
 2 files changed, 5 insertions(+), 8 deletions(-)
drivers/media/usb/au0828/au0828-core.c +4 -8
index 1fdb1601dc65..4b0a395d59aa 100644
@@ -155,9 +155,7 @@ static void au0828_unregister_media_device(struct au0828_dev *dev)
 	dev->media_dev->disable_source = NULL;
 	mutex_unlock(&mdev->graph_mutex);
 
-	media_device_unregister(dev->media_dev);
-	media_device_cleanup(dev->media_dev);
-	kfree(dev->media_dev);
+	media_device_delete(dev->media_dev, KBUILD_MODNAME);
 	dev->media_dev = NULL;
 #endif
 }
@@ -210,14 +208,10 @@ static int au0828_media_device_init(struct au0828_dev *dev,
 #ifdef CONFIG_MEDIA_CONTROLLER
 	struct media_device *mdev;
 
-	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+	mdev = media_device_usb_allocate(udev, KBUILD_MODNAME);
 	if (!mdev)
 		return -ENOMEM;
 
-	/* check if media device is already initialized */
-	if (!mdev->dev)
-		media_device_usb_init(mdev, udev, udev->product);
-
 	dev->media_dev = mdev;
 #endif
 	return 0;
@@ -480,6 +474,8 @@ static int au0828_media_device_register(struct au0828_dev *dev,
 		/* register media device */
 		ret = media_device_register(dev->media_dev);
 		if (ret) {
+			media_device_delete(dev->media_dev, KBUILD_MODNAME);
+			dev->media_dev = NULL;
 			dev_err(&udev->dev,
 				"Media Device Register Error: %d\n", ret);
 			return ret;
drivers/media/usb/au0828/au0828.h +1 -0
index 004eadef55c7..7dbe3db15ebe 100644
@@ -31,6 +31,7 @@
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-fh.h>
 #include <media/media-device.h>
+#include <media/media-dev-allocator.h>
 
 /* DVB */
 #include <media/demux.h>
Signature · 1 line
2.17.1
author
#5 lore raw

PATCH v10 3/4 media: media.h: Enable ALSA MEDIA_INTF_T* interface types

In reply to Shuah Khan

Move PCM_CAPTURE, PCM_PLAYBACK, and CONTROL ALSA MEDIA_INTF_T* interface types back into __KERNEL__ scope to get ready for adding ALSA support for these to the media controller.

---
 include/uapi/linux/media.h | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)
include/uapi/linux/media.h +15 -10
index e5d0c5c611b5..9aedb187bc48 100644
@@ -262,6 +262,11 @@ struct media_links_enum {
 #define MEDIA_INTF_T_V4L_SWRADIO		(MEDIA_INTF_T_V4L_BASE + 4)
 #define MEDIA_INTF_T_V4L_TOUCH			(MEDIA_INTF_T_V4L_BASE + 5)
 
+#define MEDIA_INTF_T_ALSA_BASE			0x00000300
+#define MEDIA_INTF_T_ALSA_PCM_CAPTURE		(MEDIA_INTF_T_ALSA_BASE)
+#define MEDIA_INTF_T_ALSA_PCM_PLAYBACK		(MEDIA_INTF_T_ALSA_BASE + 1)
+#define MEDIA_INTF_T_ALSA_CONTROL		(MEDIA_INTF_T_ALSA_BASE + 2)
+
 #if defined(__KERNEL__)
 
 /*
@@ -413,19 +418,19 @@ struct media_v2_topology {
 #define MEDIA_ENT_F_DTV_DECODER			MEDIA_ENT_F_DV_DECODER
 
 /*
- * There is still no ALSA support in the media controller. These
+ * There is still no full ALSA support in the media controller. These
  * defines should not have been added and we leave them here only
  * in case some application tries to use these defines.
+ *
+ * The ALSA defines that are in use have been moved into __KERNEL__
+ * scope. As support gets added to these interface types, they should
+ * be moved into __KERNEL__ scope with the code that uses them.
  */
-#define MEDIA_INTF_T_ALSA_BASE			0x00000300
-#define MEDIA_INTF_T_ALSA_PCM_CAPTURE		(MEDIA_INTF_T_ALSA_BASE)
-#define MEDIA_INTF_T_ALSA_PCM_PLAYBACK		(MEDIA_INTF_T_ALSA_BASE + 1)
-#define MEDIA_INTF_T_ALSA_CONTROL		(MEDIA_INTF_T_ALSA_BASE + 2)
-#define MEDIA_INTF_T_ALSA_COMPRESS		(MEDIA_INTF_T_ALSA_BASE + 3)
-#define MEDIA_INTF_T_ALSA_RAWMIDI		(MEDIA_INTF_T_ALSA_BASE + 4)
-#define MEDIA_INTF_T_ALSA_HWDEP			(MEDIA_INTF_T_ALSA_BASE + 5)
-#define MEDIA_INTF_T_ALSA_SEQUENCER		(MEDIA_INTF_T_ALSA_BASE + 6)
-#define MEDIA_INTF_T_ALSA_TIMER			(MEDIA_INTF_T_ALSA_BASE + 7)
+#define MEDIA_INTF_T_ALSA_COMPRESS             (MEDIA_INTF_T_ALSA_BASE + 3)
+#define MEDIA_INTF_T_ALSA_RAWMIDI              (MEDIA_INTF_T_ALSA_BASE + 4)
+#define MEDIA_INTF_T_ALSA_HWDEP                (MEDIA_INTF_T_ALSA_BASE + 5)
+#define MEDIA_INTF_T_ALSA_SEQUENCER            (MEDIA_INTF_T_ALSA_BASE + 6)
+#define MEDIA_INTF_T_ALSA_TIMER                (MEDIA_INTF_T_ALSA_BASE + 7)
 
 /* Obsolete symbol for media_version, no longer used in the kernel */
 #define MEDIA_API_VERSION			((0 << 16) | (1 << 8) | 0)
Signature · 1 line
2.17.1
author
#6 lore raw

PATCH v10 4/4 sound/usb: Use Media Controller API to share media resources

In reply to Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Change the ALSA driver to use the Media Controller API to share media resources with DVB, and V4L2 drivers on a AU0828 media device.

The Media Controller specific initialization is done after sound card is registered. ALSA creates Media interface and entity function graph nodes for Control, Mixer, PCM Playback, and PCM Capture devices.

snd_usb_hw_params() will call Media Controller enable source handler interface to request the media resource. If resource request is granted, it will release it from snd_usb_hw_free(). If resource is busy, -EBUSY is returned.

Media specific cleanup is done in usb_audio_disconnect().

---
 sound/usb/Kconfig        |   4 +
 sound/usb/Makefile       |   2 +
 sound/usb/card.c         |  14 ++
 sound/usb/card.h         |   3 +
 sound/usb/media.c        | 327 +++++++++++++++++++++++++++++++++++++++
 sound/usb/media.h        |  74 +++++++++
 sound/usb/mixer.h        |   3 +
 sound/usb/pcm.c          |  29 +++-
 sound/usb/quirks-table.h |   1 +
 sound/usb/stream.c       |   2 +
 sound/usb/usbaudio.h     |   6 +
 11 files changed, 461 insertions(+), 4 deletions(-)
 create mode 100644 sound/usb/media.c
 create mode 100644 sound/usb/media.h
sound/usb/Kconfig +4 -0
index f61b5662bb89..6319b544ba3a 100644
@@ -15,6 +15,7 @@ config SND_USB_AUDIO
 	select SND_RAWMIDI
 	select SND_PCM
 	select BITREVERSE
+	select SND_USB_AUDIO_USE_MEDIA_CONTROLLER if MEDIA_CONTROLLER && (MEDIA_SUPPORT=y || MEDIA_SUPPORT=SND_USB_AUDIO)
 	help
 	  Say Y here to include support for USB audio and USB MIDI
 	  devices.
@@ -22,6 +23,9 @@ config SND_USB_AUDIO
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-usb-audio.
 
+config SND_USB_AUDIO_USE_MEDIA_CONTROLLER
+	bool
+
 config SND_USB_UA101
 	tristate "Edirol UA-101/UA-1000 driver"
 	select SND_PCM
sound/usb/Makefile +2 -0
index d330f74c90e6..e1ce257ab705 100644
@@ -18,6 +18,8 @@ snd-usb-audio-objs := 	card.o \
 			quirks.o \
 			stream.o
 
+snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o
+
 snd-usbmidi-lib-objs := midi.o
 
 # Toplevel Module Dependency
sound/usb/card.c +14 -0
index 746a72e23cf9..cc6b23e282ec 100644
@@ -68,6 +68,7 @@
 #include "format.h"
 #include "power.h"
 #include "stream.h"
+#include "media.h"
 
 MODULE_AUTHOR("Takashi Iwai <[email protected]>");
 MODULE_DESCRIPTION("USB Audio");
@@ -673,6 +674,11 @@ static int usb_audio_probe(struct usb_interface *intf,
 	if (err < 0)
 		goto __error;
 
+	if (quirk && quirk->shares_media_device) {
+		/* don't want to fail when snd_media_device_create() fails */
+		snd_media_device_create(chip, intf);
+	}
+
 	usb_chip[chip->index] = chip;
 	chip->num_interfaces++;
 	usb_set_intfdata(intf, chip);
@@ -732,6 +738,14 @@ static void usb_audio_disconnect(struct usb_interface *intf)
 		list_for_each(p, &chip->midi_list) {
 			snd_usbmidi_disconnect(p);
 		}
+		/*
+		 * Nice to check quirk && quirk->shares_media_device and
+		 * then call the snd_media_device_delete(). Don't have
+		 * access to the quirk here. snd_media_device_delete()
+		 * accesses mixer_list
+		 */
+		snd_media_device_delete(chip);
+
 		/* release mixer resources */
 		list_for_each_entry(mixer, &chip->mixer_list, list) {
 			snd_usb_mixer_disconnect(mixer);
sound/usb/card.h +3 -0
index ac785d15ced4..5dd3538ed6b5 100644
@@ -108,6 +108,8 @@ struct snd_usb_endpoint {
 	struct list_head list;
 };
 
+struct media_ctl;
+
 struct snd_usb_substream {
 	struct snd_usb_stream *stream;
 	struct usb_device *dev;
@@ -160,6 +162,7 @@ struct snd_usb_substream {
 	} dsd_dop;
 
 	bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */
+	struct media_ctl *media_ctl;
 };
 
 struct snd_usb_stream {
sound/usb/media.c new file +327 -0

327 lines not on this page · raw message

sound/usb/media.h new file +74 -0

74 lines not on this page · raw message

sound/usb/mixer.h +3 -0
index 3d12af8bf191..394cd9107507 100644
@@ -4,6 +4,8 @@
 
 #include <sound/info.h>
 
+struct media_mixer_ctl;
+
 struct usb_mixer_interface {
 	struct snd_usb_audio *chip;
 	struct usb_host_interface *hostif;
@@ -23,6 +25,7 @@ struct usb_mixer_interface {
 	struct urb *rc_urb;
 	struct usb_ctrlrequest *rc_setup_packet;
 	u8 rc_buffer[6];
+	struct media_mixer_ctl *media_mixer_ctl;
 
 	bool disconnected;
 };
sound/usb/pcm.c +25 -4
index 382847154227..538ed10ab66d 100644
@@ -35,6 +35,7 @@
 #include "pcm.h"
 #include "clock.h"
 #include "power.h"
+#include "media.h"
 
 #define SUBSTREAM_FLAG_DATA_EP_STARTED	0
 #define SUBSTREAM_FLAG_SYNC_EP_STARTED	1
@@ -776,6 +777,10 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
 	struct audioformat *fmt;
 	int ret;
 
+	ret = snd_media_start_pipeline(subs);
+	if (ret)
+		return ret;
+
 	if (snd_usb_use_vmalloc)
 		ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
 						       params_buffer_bytes(hw_params));
@@ -783,7 +788,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
 		ret = snd_pcm_lib_malloc_pages(substream,
 					       params_buffer_bytes(hw_params));
 	if (ret < 0)
-		return ret;
+		goto stop_pipeline;
 
 	subs->pcm_format = params_format(hw_params);
 	subs->period_bytes = params_period_bytes(hw_params);
@@ -797,12 +802,13 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
 		dev_dbg(&subs->dev->dev,
 			"cannot set format: format = %#x, rate = %d, channels = %d\n",
 			   subs->pcm_format, subs->cur_rate, subs->channels);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto stop_pipeline;
 	}
 
 	ret = snd_usb_lock_shutdown(subs->stream->chip);
 	if (ret < 0)
-		return ret;
+		goto stop_pipeline;
 
 	ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0);
 	if (ret < 0)
@@ -818,6 +824,12 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
 
  unlock:
 	snd_usb_unlock_shutdown(subs->stream->chip);
+	if (ret < 0)
+		goto stop_pipeline;
+	return ret;
+
+ stop_pipeline:
+	snd_media_stop_pipeline(subs);
 	return ret;
 }
 
@@ -830,6 +842,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_usb_substream *subs = substream->runtime->private_data;
 
+	snd_media_stop_pipeline(subs);
 	subs->cur_audiofmt = NULL;
 	subs->cur_rate = 0;
 	subs->period_bytes = 0;
@@ -1302,6 +1315,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
 	struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_usb_substream *subs = &as->substream[direction];
+	int ret;
 
 	subs->interface = -1;
 	subs->altset_idx = 0;
@@ -1315,7 +1329,13 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
 	subs->dsd_dop.channel = 0;
 	subs->dsd_dop.marker = 1;
 
-	return setup_hw_info(runtime, subs);
+	ret = setup_hw_info(runtime, subs);
+	if (ret == 0) {
+		ret = snd_media_stream_init(subs, as->pcm, direction);
+		if (ret)
+			snd_usb_autosuspend(subs->stream->chip);
+	}
+	return ret;
 }
 
 static int snd_usb_pcm_close(struct snd_pcm_substream *substream)
@@ -1326,6 +1346,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream)
 	int ret;
 
 	stop_endpoints(subs, true);
+	snd_media_stop_pipeline(subs);
 
 	if (!as->chip->keep_iface &&
 	    subs->interface >= 0 &&
sound/usb/quirks-table.h +1 -0
index b345beb447bd..6835665d311f 100644
@@ -2887,6 +2887,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
 		.product_name = pname, \
 		.ifnum = QUIRK_ANY_INTERFACE, \
 		.type = QUIRK_AUDIO_ALIGN_TRANSFER, \
+		.shares_media_device = 1, \
 	} \
 }
 
sound/usb/stream.c +2 -0
index d9e3de495c16..9f1623e37fb3 100644
@@ -38,6 +38,7 @@
 #include "clock.h"
 #include "stream.h"
 #include "power.h"
+#include "media.h"
 
 /*
  * free a substream
@@ -55,6 +56,7 @@ static void free_substream(struct snd_usb_substream *subs)
 	}
 	kfree(subs->rate_list.list);
 	kfree(subs->str_pd);
+	snd_media_stream_delete(subs);
 }
 
 
sound/usb/usbaudio.h +6 -0
index b9faeca645fd..0968a45c8925 100644
@@ -30,6 +30,9 @@
  *
  */
 
+struct media_device;
+struct media_intf_devnode;
+
 struct snd_usb_audio {
 	int index;
 	struct usb_device *dev;
@@ -66,6 +69,8 @@ struct snd_usb_audio {
 					 */
 
 	struct usb_host_interface *ctrl_intf;	/* the audio control interface */
+	struct media_device *media_dev;
+	struct media_intf_devnode *ctl_intf_media_devnode;
 };
 
 #define usb_audio_err(chip, fmt, args...) \
@@ -117,6 +122,7 @@ struct snd_usb_audio_quirk {
 	const char *profile_name;	/* override the card->longname */
 	int16_t ifnum;
 	uint16_t type;
+	bool shares_media_device;
 	const void *data;
 };
 
Signature · 1 line
2.17.1
#7 lore raw
In reply to Shuah Khan

Hi Shuah,

Quoted 11 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Thanks for the update. I have to apologise I haven't ended up reviewing the set for some time. After taking a look at the current version, I'm happy to see that a number of issues recognised during earlier review rounds have been addressed.

Would you happen to have a media graph (media-ctl --print-dot and media-ctl -p) from the device? That'd help understanding the device a bit better for those who are not familiar with it.

Quoted 65 lines

- This patch series is tested on 5.0-rc3 and addresses comments on v9 series from Hans Verkuil.
- v9 was tested on 4.20-rc6.
- Tested sharing resources with kaffeine, vlc, xawtv, tvtime, and arecord. When analog is streaming, digital and audio user-space applications detect that the tuner is busy and exit. When digital is streaming, analog and audio applications detect that the tuner is busy and exit. When arecord is owns the tuner, digital and analog detect that the tuner is busy and exit.
- Tested media device allocator API with bind/unbind testing on snd-usb-audio and au0828 drivers to make sure /dev/mediaX is released only when the last driver is unbound.
- Addressed review comments from Hans on the RFC v8 (rebased on 4.19)
- Updated change log to describe the use-case more clearly.
- No changes to 0001,0002 code since the v7 referenced below.
- 0003 is a new patch to enable ALSA defines that have been disabled for kernel between 4.9 and 4.19.
- Minor merge conflict resolution in 0004.
- Added SPDX to new files.

Changes since v9:
- Patch 1: Fix mutex assert warning from find_module() calls. This code was written before the change to find_module() that requires callers to hold module_mutex. I missed this during my testing on 4.20-rc6. Hans Verkuil reported the problem.
- Patch 4: sound/usb: Initializes all the entities it can before registering the device based on comments from Hans Verkuil
- Carried Reviewed-by tag from Takashi Iwai for the sound from v9.
- No changes to Patches 2 and 3.

References:
https://lkml.org/lkml/2018/11/2/169
https://www.mail-archive.com/[email protected]/msg105854.html

Shuah Khan (4):
media: Media Device Allocator API
media: change au0828 to use Media Device Allocator API
media: media.h: Enable ALSA MEDIA_INTF_T* interface types sound/usb: Use Media Controller API to share media resources

 Documentation/media/kapi/mc-core.rst   |  41 ++++
 drivers/media/Makefile                 |   4 +
 drivers/media/media-dev-allocator.c    | 144 +++++++++++
 drivers/media/usb/au0828/au0828-core.c |  12 +-
 drivers/media/usb/au0828/au0828.h      |   1 +
 include/media/media-dev-allocator.h    |  53 ++++
 include/uapi/linux/media.h             |  25 +-
 sound/usb/Kconfig                      |   4 +
 sound/usb/Makefile                     |   2 +
 sound/usb/card.c                       |  14 ++
 sound/usb/card.h                       |   3 +
 sound/usb/media.c                      | 327 +++++++++++++++++++++++++
 sound/usb/media.h                      |  74 ++++++
 sound/usb/mixer.h                      |   3 +
 sound/usb/pcm.c                        |  29 ++-
 sound/usb/quirks-table.h               |   1 +
 sound/usb/stream.c                     |   2 +
 sound/usb/usbaudio.h                   |   6 +
 18 files changed, 723 insertions(+), 22 deletions(-)
 create mode 100644 drivers/media/media-dev-allocator.c
 create mode 100644 include/media/media-dev-allocator.h
 create mode 100644 sound/usb/media.c
 create mode 100644 sound/usb/media.h
Signature · 3 lines
Kind regards,

Sakari Ailus
author
#8 lore raw

Hi Sakari,

Quoted 15 lines · Sakari Ailus

Hi Shuah,

Quoted 11 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Quoted 9 lines

Thanks for the update. I have to apologise I haven't ended up reviewing the set for some time. After taking a look at the current version, I'm happy to see that a number of issues recognised during earlier review rounds have been addressed.

Would you happen to have a media graph (media-ctl --print-dot and media-ctl -p) from the device? That'd help understanding the device a bit better for those who are not familiar with it.

Please see the attached files for the below. It came in very handy for testing partial graphs as I did bind and unbind of the drivers in this mix.

media-ctl --print-dot and
media-ctl --print-topology

thanks,
-- Shuah

Media controller API version 5.0.0

Media device information
------------------------

driver          au0828
model           WinTV HVR-950
serial          4035198866
bus info        usb-0000:00:1a.0-1.4
hw revision     0x5
driver version  5.0.0

Device topology
- entity 1: au8522 13-0047 (3 pads, 6 links)

            type V4L2 subdev subtype Decoder flags 0
	pad0: Sink
		<- "Xceive XC5000":1 []
		<- "Composite":0 []
		<- "S-Video":0 []
	pad1: Source
		-> "au0828a video":0 [ENABLED]
		-> "au0828a vbi":0 [ENABLED]
	pad2: Source
		-> "USB Mixer":0 [ENABLED]

- entity 5: Xceive XC5000 (3 pads, 3 links)

            type V4L2 subdev subtype Tuner flags 0
	pad0: Sink
		-> "Auvitek AU8522 QAM/8VSB Fronten":0 []
		<- "Television":0 [ENABLED]
	pad1: Source
		-> "au8522 13-0047":0 []
	pad2: Source

- entity 9: Television (1 pad, 1 link)
type Node subtype Unknown flags 2
pad0: Source
-> "Xceive XC5000":0 [ENABLED]

- entity 11: Composite (1 pad, 1 link)
type Node subtype Unknown flags 2
pad0: Source
-> "au8522 13-0047":0 []

- entity 13: S-Video (1 pad, 1 link)
type Node subtype Unknown flags 2
pad0: Source
-> "au8522 13-0047":0 []

- entity 15: au0828a video (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video0
pad0: Sink
<- "au8522 13-0047":1 [ENABLED]

- entity 19: au0828a vbi (1 pad, 1 link)
type Node subtype Unknown flags 0
device node name /dev/vbi0
pad0: Sink
<- "au8522 13-0047":1 [ENABLED]

- entity 35: Auvitek AU8522 QAM/8VSB Fronten (2 pads, 2 links)

             type Node subtype Unknown flags 0
	pad0: Sink
		<- "Xceive XC5000":0 []
	pad1: Source
		-> "dvb-demux":0 [ENABLED]

- entity 40: demux-tsout #0 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":1 []

- entity 42: demux-tsout #1 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":2 []

- entity 44: demux-tsout #2 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":3 []

- entity 46: demux-tsout #3 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":4 []

- entity 48: demux-tsout #4 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":5 []

- entity 50: demux-tsout #5 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":6 []

- entity 52: demux-tsout #6 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":7 []

- entity 54: demux-tsout #7 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":8 []

- entity 56: demux-tsout #8 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":9 []

- entity 58: demux-tsout #9 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":10 []

- entity 60: demux-tsout #10 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":11 []

- entity 62: demux-tsout #11 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":12 []

- entity 64: demux-tsout #12 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":13 []

- entity 66: demux-tsout #13 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":14 []

- entity 68: demux-tsout #14 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":15 []

- entity 70: demux-tsout #15 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":16 []

- entity 72: demux-tsout #16 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":17 []

- entity 74: demux-tsout #17 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":18 []

- entity 76: demux-tsout #18 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":19 []

- entity 78: demux-tsout #19 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":20 []

- entity 80: demux-tsout #20 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":21 []

- entity 82: demux-tsout #21 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":22 []

- entity 84: demux-tsout #22 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":23 []

- entity 86: demux-tsout #23 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":24 []

- entity 88: demux-tsout #24 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":25 []

- entity 90: demux-tsout #25 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":26 []

- entity 92: demux-tsout #26 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":27 []

- entity 94: demux-tsout #27 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":28 []

- entity 96: demux-tsout #28 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":29 []

- entity 98: demux-tsout #29 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":30 []

- entity 100: demux-tsout #30 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":31 []

- entity 102: demux-tsout #31 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":32 []

- entity 104: demux-tsout #32 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":33 []

- entity 106: demux-tsout #33 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":34 []

- entity 108: demux-tsout #34 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":35 []

- entity 110: demux-tsout #35 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":36 []

- entity 112: demux-tsout #36 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":37 []

- entity 114: demux-tsout #37 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":38 []

- entity 116: demux-tsout #38 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":39 []

- entity 118: demux-tsout #39 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":40 []

- entity 120: demux-tsout #40 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":41 []

- entity 122: demux-tsout #41 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":42 []

- entity 124: demux-tsout #42 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":43 []

- entity 126: demux-tsout #43 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":44 []

- entity 128: demux-tsout #44 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":45 []

- entity 130: demux-tsout #45 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":46 []

- entity 132: demux-tsout #46 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":47 []

- entity 134: demux-tsout #47 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":48 []

- entity 136: demux-tsout #48 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":49 []

- entity 138: demux-tsout #49 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":50 []

- entity 140: demux-tsout #50 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":51 []

- entity 142: demux-tsout #51 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":52 []

- entity 144: demux-tsout #52 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":53 []

- entity 146: demux-tsout #53 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":54 []

- entity 148: demux-tsout #54 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":55 []

- entity 150: demux-tsout #55 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":56 []

- entity 152: demux-tsout #56 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":57 []

- entity 154: demux-tsout #57 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":58 []

- entity 156: demux-tsout #58 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":59 []

- entity 158: demux-tsout #59 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":60 []

- entity 160: demux-tsout #60 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":61 []

- entity 162: demux-tsout #61 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":62 []

- entity 164: demux-tsout #62 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":63 []

- entity 166: demux-tsout #63 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":64 []

- entity 168: demux-tsout #64 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":65 []

- entity 170: demux-tsout #65 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":66 []

- entity 172: demux-tsout #66 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":67 []

- entity 174: demux-tsout #67 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":68 []

- entity 176: demux-tsout #68 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":69 []

- entity 178: demux-tsout #69 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":70 []

- entity 180: demux-tsout #70 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":71 []

- entity 182: demux-tsout #71 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":72 []

- entity 184: demux-tsout #72 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":73 []

- entity 186: demux-tsout #73 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":74 []

- entity 188: demux-tsout #74 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":75 []

- entity 190: demux-tsout #75 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":76 []

- entity 192: demux-tsout #76 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":77 []

- entity 194: demux-tsout #77 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":78 []

- entity 196: demux-tsout #78 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":79 []

- entity 198: demux-tsout #79 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":80 []

- entity 200: demux-tsout #80 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":81 []

- entity 202: demux-tsout #81 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":82 []

- entity 204: demux-tsout #82 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":83 []

- entity 206: demux-tsout #83 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":84 []

- entity 208: demux-tsout #84 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":85 []

- entity 210: demux-tsout #85 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":86 []

- entity 212: demux-tsout #86 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":87 []

- entity 214: demux-tsout #87 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":88 []

- entity 216: demux-tsout #88 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":89 []

- entity 218: demux-tsout #89 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":90 []

- entity 220: demux-tsout #90 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":91 []

- entity 222: demux-tsout #91 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":92 []

- entity 224: demux-tsout #92 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":93 []

- entity 226: demux-tsout #93 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":94 []

- entity 228: demux-tsout #94 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":95 []

- entity 230: demux-tsout #95 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":96 []

- entity 232: demux-tsout #96 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":97 []

- entity 234: demux-tsout #97 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":98 []

- entity 236: demux-tsout #98 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":99 []

- entity 238: demux-tsout #99 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":100 []

- entity 240: demux-tsout #100 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":101 []

- entity 242: demux-tsout #101 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":102 []

- entity 244: demux-tsout #102 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":103 []

- entity 246: demux-tsout #103 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":104 []

- entity 248: demux-tsout #104 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":105 []

- entity 250: demux-tsout #105 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":106 []

- entity 252: demux-tsout #106 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":107 []

- entity 254: demux-tsout #107 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":108 []

- entity 256: demux-tsout #108 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":109 []

- entity 258: demux-tsout #109 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":110 []

- entity 260: demux-tsout #110 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":111 []

- entity 262: demux-tsout #111 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":112 []

- entity 264: demux-tsout #112 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":113 []

- entity 266: demux-tsout #113 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":114 []

- entity 268: demux-tsout #114 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":115 []

- entity 270: demux-tsout #115 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":116 []

- entity 272: demux-tsout #116 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":117 []

- entity 274: demux-tsout #117 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":118 []

- entity 276: demux-tsout #118 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":119 []

- entity 278: demux-tsout #119 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":120 []

- entity 280: demux-tsout #120 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":121 []

- entity 282: demux-tsout #121 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":122 []

- entity 284: demux-tsout #122 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":123 []

- entity 286: demux-tsout #123 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":124 []

- entity 288: demux-tsout #124 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":125 []

- entity 290: demux-tsout #125 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":126 []

- entity 292: demux-tsout #126 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":127 []

- entity 294: demux-tsout #127 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":128 []

- entity 296: demux-tsout #128 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":129 []

- entity 298: demux-tsout #129 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":130 []

- entity 300: demux-tsout #130 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":131 []

- entity 302: demux-tsout #131 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":132 []

- entity 304: demux-tsout #132 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":133 []

- entity 306: demux-tsout #133 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":134 []

- entity 308: demux-tsout #134 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":135 []

- entity 310: demux-tsout #135 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":136 []

- entity 312: demux-tsout #136 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":137 []

- entity 314: demux-tsout #137 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":138 []

- entity 316: demux-tsout #138 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":139 []

- entity 318: demux-tsout #139 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":140 []

- entity 320: demux-tsout #140 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":141 []

- entity 322: demux-tsout #141 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":142 []

- entity 324: demux-tsout #142 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":143 []

- entity 326: demux-tsout #143 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":144 []

- entity 328: demux-tsout #144 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":145 []

- entity 330: demux-tsout #145 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":146 []

- entity 332: demux-tsout #146 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":147 []

- entity 334: demux-tsout #147 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":148 []

- entity 336: demux-tsout #148 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":149 []

- entity 338: demux-tsout #149 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":150 []

- entity 340: demux-tsout #150 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":151 []

- entity 342: demux-tsout #151 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":152 []

- entity 344: demux-tsout #152 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":153 []

- entity 346: demux-tsout #153 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":154 []

- entity 348: demux-tsout #154 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":155 []

- entity 350: demux-tsout #155 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":156 []

- entity 352: demux-tsout #156 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":157 []

- entity 354: demux-tsout #157 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":158 []

- entity 356: demux-tsout #158 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":159 []

- entity 358: demux-tsout #159 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":160 []

- entity 360: demux-tsout #160 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":161 []

- entity 362: demux-tsout #161 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":162 []

- entity 364: demux-tsout #162 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":163 []

- entity 366: demux-tsout #163 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":164 []

- entity 368: demux-tsout #164 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":165 []

- entity 370: demux-tsout #165 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":166 []

- entity 372: demux-tsout #166 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":167 []

- entity 374: demux-tsout #167 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":168 []

- entity 376: demux-tsout #168 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":169 []

- entity 378: demux-tsout #169 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":170 []

- entity 380: demux-tsout #170 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":171 []

- entity 382: demux-tsout #171 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":172 []

- entity 384: demux-tsout #172 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":173 []

- entity 386: demux-tsout #173 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":174 []

- entity 388: demux-tsout #174 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":175 []

- entity 390: demux-tsout #175 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":176 []

- entity 392: demux-tsout #176 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":177 []

- entity 394: demux-tsout #177 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":178 []

- entity 396: demux-tsout #178 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":179 []

- entity 398: demux-tsout #179 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":180 []

- entity 400: demux-tsout #180 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":181 []

- entity 402: demux-tsout #181 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":182 []

- entity 404: demux-tsout #182 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":183 []

- entity 406: demux-tsout #183 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":184 []

- entity 408: demux-tsout #184 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":185 []

- entity 410: demux-tsout #185 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":186 []

- entity 412: demux-tsout #186 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":187 []

- entity 414: demux-tsout #187 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":188 []

- entity 416: demux-tsout #188 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":189 []

- entity 418: demux-tsout #189 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":190 []

- entity 420: demux-tsout #190 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":191 []

- entity 422: demux-tsout #191 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":192 []

- entity 424: demux-tsout #192 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":193 []

- entity 426: demux-tsout #193 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":194 []

- entity 428: demux-tsout #194 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":195 []

- entity 430: demux-tsout #195 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":196 []

- entity 432: demux-tsout #196 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":197 []

- entity 434: demux-tsout #197 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":198 []

- entity 436: demux-tsout #198 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":199 []

- entity 438: demux-tsout #199 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":200 []

- entity 440: demux-tsout #200 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":201 []

- entity 442: demux-tsout #201 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":202 []

- entity 444: demux-tsout #202 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":203 []

- entity 446: demux-tsout #203 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":204 []

- entity 448: demux-tsout #204 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":205 []

- entity 450: demux-tsout #205 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":206 []

- entity 452: demux-tsout #206 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":207 []

- entity 454: demux-tsout #207 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":208 []

- entity 456: demux-tsout #208 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":209 []

- entity 458: demux-tsout #209 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":210 []

- entity 460: demux-tsout #210 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":211 []

- entity 462: demux-tsout #211 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":212 []

- entity 464: demux-tsout #212 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":213 []

- entity 466: demux-tsout #213 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":214 []

- entity 468: demux-tsout #214 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":215 []

- entity 470: demux-tsout #215 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":216 []

- entity 472: demux-tsout #216 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":217 []

- entity 474: demux-tsout #217 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":218 []

- entity 476: demux-tsout #218 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":219 []

- entity 478: demux-tsout #219 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":220 []

- entity 480: demux-tsout #220 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":221 []

- entity 482: demux-tsout #221 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":222 []

- entity 484: demux-tsout #222 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":223 []

- entity 486: demux-tsout #223 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":224 []

- entity 488: demux-tsout #224 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":225 []

- entity 490: demux-tsout #225 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":226 []

- entity 492: demux-tsout #226 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":227 []

- entity 494: demux-tsout #227 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":228 []

- entity 496: demux-tsout #228 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":229 []

- entity 498: demux-tsout #229 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":230 []

- entity 500: demux-tsout #230 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":231 []

- entity 502: demux-tsout #231 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":232 []

- entity 504: demux-tsout #232 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":233 []

- entity 506: demux-tsout #233 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":234 []

- entity 508: demux-tsout #234 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":235 []

- entity 510: demux-tsout #235 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":236 []

- entity 512: demux-tsout #236 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":237 []

- entity 514: demux-tsout #237 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":238 []

- entity 516: demux-tsout #238 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":239 []

- entity 518: demux-tsout #239 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":240 []

- entity 520: demux-tsout #240 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":241 []

- entity 522: demux-tsout #241 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":242 []

- entity 524: demux-tsout #242 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":243 []

- entity 526: demux-tsout #243 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":244 []

- entity 528: demux-tsout #244 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":245 []

- entity 530: demux-tsout #245 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":246 []

- entity 532: demux-tsout #246 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":247 []

- entity 534: demux-tsout #247 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":248 []

- entity 536: demux-tsout #248 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":249 []

- entity 538: demux-tsout #249 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":250 []

- entity 540: demux-tsout #250 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":251 []

- entity 542: demux-tsout #251 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":252 []

- entity 544: demux-tsout #252 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":253 []

- entity 546: demux-tsout #253 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":254 []

- entity 548: demux-tsout #254 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":255 []

- entity 550: demux-tsout #255 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":256 []

- entity 552: dvb-demux (257 pads, 513 links)

              type Node subtype Unknown flags 0
	pad0: Sink
		<- "Auvitek AU8522 QAM/8VSB Fronten":1 [ENABLED]
	pad1: Source
		-> "demux-tsout #0":0 []
		-> "dvr-tsout #0":0 []
	pad2: Source
		-> "demux-tsout #1":0 []
		-> "dvr-tsout #1":0 []
	pad3: Source
		-> "demux-tsout #2":0 []
		-> "dvr-tsout #2":0 []
	pad4: Source
		-> "demux-tsout #3":0 []
		-> "dvr-tsout #3":0 []
	pad5: Source
		-> "demux-tsout #4":0 []
		-> "dvr-tsout #4":0 []
	pad6: Source
		-> "demux-tsout #5":0 []
		-> "dvr-tsout #5":0 []
	pad7: Source
		-> "demux-tsout #6":0 []
		-> "dvr-tsout #6":0 []
	pad8: Source
		-> "demux-tsout #7":0 []
		-> "dvr-tsout #7":0 []
	pad9: Source
		-> "demux-tsout #8":0 []
		-> "dvr-tsout #8":0 []
	pad10: Source
		-> "demux-tsout #9":0 []
		-> "dvr-tsout #9":0 []
	pad11: Source
		-> "demux-tsout #10":0 []
		-> "dvr-tsout #10":0 []
	pad12: Source
		-> "demux-tsout #11":0 []
		-> "dvr-tsout #11":0 []
	pad13: Source
		-> "demux-tsout #12":0 []
		-> "dvr-tsout #12":0 []
	pad14: Source
		-> "demux-tsout #13":0 []
		-> "dvr-tsout #13":0 []
	pad15: Source
		-> "demux-tsout #14":0 []
		-> "dvr-tsout #14":0 []
	pad16: Source
		-> "demux-tsout #15":0 []
		-> "dvr-tsout #15":0 []
	pad17: Source
		-> "demux-tsout #16":0 []
		-> "dvr-tsout #16":0 []
	pad18: Source
		-> "demux-tsout #17":0 []
		-> "dvr-tsout #17":0 []
	pad19: Source
		-> "demux-tsout #18":0 []
		-> "dvr-tsout #18":0 []
	pad20: Source
		-> "demux-tsout #19":0 []
		-> "dvr-tsout #19":0 []
	pad21: Source
		-> "demux-tsout #20":0 []
		-> "dvr-tsout #20":0 []
	pad22: Source
		-> "demux-tsout #21":0 []
		-> "dvr-tsout #21":0 []
	pad23: Source
		-> "demux-tsout #22":0 []
		-> "dvr-tsout #22":0 []
	pad24: Source
		-> "demux-tsout #23":0 []
		-> "dvr-tsout #23":0 []
	pad25: Source
		-> "demux-tsout #24":0 []
		-> "dvr-tsout #24":0 []
	pad26: Source
		-> "demux-tsout #25":0 []
		-> "dvr-tsout #25":0 []
	pad27: Source
		-> "demux-tsout #26":0 []
		-> "dvr-tsout #26":0 []
	pad28: Source
		-> "demux-tsout #27":0 []
		-> "dvr-tsout #27":0 []
	pad29: Source
		-> "demux-tsout #28":0 []
		-> "dvr-tsout #28":0 []
	pad30: Source
		-> "demux-tsout #29":0 []
		-> "dvr-tsout #29":0 []
	pad31: Source
		-> "demux-tsout #30":0 []
		-> "dvr-tsout #30":0 []
	pad32: Source
		-> "demux-tsout #31":0 []
		-> "dvr-tsout #31":0 []
	pad33: Source
		-> "demux-tsout #32":0 []
		-> "dvr-tsout #32":0 []
	pad34: Source
		-> "demux-tsout #33":0 []
		-> "dvr-tsout #33":0 []
	pad35: Source
		-> "demux-tsout #34":0 []
		-> "dvr-tsout #34":0 []
	pad36: Source
		-> "demux-tsout #35":0 []
		-> "dvr-tsout #35":0 []
	pad37: Source
		-> "demux-tsout #36":0 []
		-> "dvr-tsout #36":0 []
	pad38: Source
		-> "demux-tsout #37":0 []
		-> "dvr-tsout #37":0 []
	pad39: Source
		-> "demux-tsout #38":0 []
		-> "dvr-tsout #38":0 []
	pad40: Source
		-> "demux-tsout #39":0 []
		-> "dvr-tsout #39":0 []
	pad41: Source
		-> "demux-tsout #40":0 []
		-> "dvr-tsout #40":0 []
	pad42: Source
		-> "demux-tsout #41":0 []
		-> "dvr-tsout #41":0 []
	pad43: Source
		-> "demux-tsout #42":0 []
		-> "dvr-tsout #42":0 []
	pad44: Source
		-> "demux-tsout #43":0 []
		-> "dvr-tsout #43":0 []
	pad45: Source
		-> "demux-tsout #44":0 []
		-> "dvr-tsout #44":0 []
	pad46: Source
		-> "demux-tsout #45":0 []
		-> "dvr-tsout #45":0 []
	pad47: Source
		-> "demux-tsout #46":0 []
		-> "dvr-tsout #46":0 []
	pad48: Source
		-> "demux-tsout #47":0 []
		-> "dvr-tsout #47":0 []
	pad49: Source
		-> "demux-tsout #48":0 []
		-> "dvr-tsout #48":0 []
	pad50: Source
		-> "demux-tsout #49":0 []
		-> "dvr-tsout #49":0 []
	pad51: Source
		-> "demux-tsout #50":0 []
		-> "dvr-tsout #50":0 []
	pad52: Source
		-> "demux-tsout #51":0 []
		-> "dvr-tsout #51":0 []
	pad53: Source
		-> "demux-tsout #52":0 []
		-> "dvr-tsout #52":0 []
	pad54: Source
		-> "demux-tsout #53":0 []
		-> "dvr-tsout #53":0 []
	pad55: Source
		-> "demux-tsout #54":0 []
		-> "dvr-tsout #54":0 []
	pad56: Source
		-> "demux-tsout #55":0 []
		-> "dvr-tsout #55":0 []
	pad57: Source
		-> "demux-tsout #56":0 []
		-> "dvr-tsout #56":0 []
	pad58: Source
		-> "demux-tsout #57":0 []
		-> "dvr-tsout #57":0 []
	pad59: Source
		-> "demux-tsout #58":0 []
		-> "dvr-tsout #58":0 []
	pad60: Source
		-> "demux-tsout #59":0 []
		-> "dvr-tsout #59":0 []
	pad61: Source
		-> "demux-tsout #60":0 []
		-> "dvr-tsout #60":0 []
	pad62: Source
		-> "demux-tsout #61":0 []
		-> "dvr-tsout #61":0 []
	pad63: Source
		-> "demux-tsout #62":0 []
		-> "dvr-tsout #62":0 []
	pad64: Source
		-> "demux-tsout #63":0 []
		-> "dvr-tsout #63":0 []
	pad65: Source
		-> "demux-tsout #64":0 []
		-> "dvr-tsout #64":0 []
	pad66: Source
		-> "demux-tsout #65":0 []
		-> "dvr-tsout #65":0 []
	pad67: Source
		-> "demux-tsout #66":0 []
		-> "dvr-tsout #66":0 []
	pad68: Source
		-> "demux-tsout #67":0 []
		-> "dvr-tsout #67":0 []
	pad69: Source
		-> "demux-tsout #68":0 []
		-> "dvr-tsout #68":0 []
	pad70: Source
		-> "demux-tsout #69":0 []
		-> "dvr-tsout #69":0 []
	pad71: Source
		-> "demux-tsout #70":0 []
		-> "dvr-tsout #70":0 []
	pad72: Source
		-> "demux-tsout #71":0 []
		-> "dvr-tsout #71":0 []
	pad73: Source
		-> "demux-tsout #72":0 []
		-> "dvr-tsout #72":0 []
	pad74: Source
		-> "demux-tsout #73":0 []
		-> "dvr-tsout #73":0 []
	pad75: Source
		-> "demux-tsout #74":0 []
		-> "dvr-tsout #74":0 []
	pad76: Source
		-> "demux-tsout #75":0 []
		-> "dvr-tsout #75":0 []
	pad77: Source
		-> "demux-tsout #76":0 []
		-> "dvr-tsout #76":0 []
	pad78: Source
		-> "demux-tsout #77":0 []
		-> "dvr-tsout #77":0 []
	pad79: Source
		-> "demux-tsout #78":0 []
		-> "dvr-tsout #78":0 []
	pad80: Source
		-> "demux-tsout #79":0 []
		-> "dvr-tsout #79":0 []
	pad81: Source
		-> "demux-tsout #80":0 []
		-> "dvr-tsout #80":0 []
	pad82: Source
		-> "demux-tsout #81":0 []
		-> "dvr-tsout #81":0 []
	pad83: Source
		-> "demux-tsout #82":0 []
		-> "dvr-tsout #82":0 []
	pad84: Source
		-> "demux-tsout #83":0 []
		-> "dvr-tsout #83":0 []
	pad85: Source
		-> "demux-tsout #84":0 []
		-> "dvr-tsout #84":0 []
	pad86: Source
		-> "demux-tsout #85":0 []
		-> "dvr-tsout #85":0 []
	pad87: Source
		-> "demux-tsout #86":0 []
		-> "dvr-tsout #86":0 []
	pad88: Source
		-> "demux-tsout #87":0 []
		-> "dvr-tsout #87":0 []
	pad89: Source
		-> "demux-tsout #88":0 []
		-> "dvr-tsout #88":0 []
	pad90: Source
		-> "demux-tsout #89":0 []
		-> "dvr-tsout #89":0 []
	pad91: Source
		-> "demux-tsout #90":0 []
		-> "dvr-tsout #90":0 []
	pad92: Source
		-> "demux-tsout #91":0 []
		-> "dvr-tsout #91":0 []
	pad93: Source
		-> "demux-tsout #92":0 []
		-> "dvr-tsout #92":0 []
	pad94: Source
		-> "demux-tsout #93":0 []
		-> "dvr-tsout #93":0 []
	pad95: Source
		-> "demux-tsout #94":0 []
		-> "dvr-tsout #94":0 []
	pad96: Source
		-> "demux-tsout #95":0 []
		-> "dvr-tsout #95":0 []
	pad97: Source
		-> "demux-tsout #96":0 []
		-> "dvr-tsout #96":0 []
	pad98: Source
		-> "demux-tsout #97":0 []
		-> "dvr-tsout #97":0 []
	pad99: Source
		-> "demux-tsout #98":0 []
		-> "dvr-tsout #98":0 []
	pad100: Source
		-> "demux-tsout #99":0 []
		-> "dvr-tsout #99":0 []
	pad101: Source
		-> "demux-tsout #100":0 []
		-> "dvr-tsout #100":0 []
	pad102: Source
		-> "demux-tsout #101":0 []
		-> "dvr-tsout #101":0 []
	pad103: Source
		-> "demux-tsout #102":0 []
		-> "dvr-tsout #102":0 []
	pad104: Source
		-> "demux-tsout #103":0 []
		-> "dvr-tsout #103":0 []
	pad105: Source
		-> "demux-tsout #104":0 []
		-> "dvr-tsout #104":0 []
	pad106: Source
		-> "demux-tsout #105":0 []
		-> "dvr-tsout #105":0 []
	pad107: Source
		-> "demux-tsout #106":0 []
		-> "dvr-tsout #106":0 []
	pad108: Source
		-> "demux-tsout #107":0 []
		-> "dvr-tsout #107":0 []
	pad109: Source
		-> "demux-tsout #108":0 []
		-> "dvr-tsout #108":0 []
	pad110: Source
		-> "demux-tsout #109":0 []
		-> "dvr-tsout #109":0 []
	pad111: Source
		-> "demux-tsout #110":0 []
		-> "dvr-tsout #110":0 []
	pad112: Source
		-> "demux-tsout #111":0 []
		-> "dvr-tsout #111":0 []
	pad113: Source
		-> "demux-tsout #112":0 []
		-> "dvr-tsout #112":0 []
	pad114: Source
		-> "demux-tsout #113":0 []
		-> "dvr-tsout #113":0 []
	pad115: Source
		-> "demux-tsout #114":0 []
		-> "dvr-tsout #114":0 []
	pad116: Source
		-> "demux-tsout #115":0 []
		-> "dvr-tsout #115":0 []
	pad117: Source
		-> "demux-tsout #116":0 []
		-> "dvr-tsout #116":0 []
	pad118: Source
		-> "demux-tsout #117":0 []
		-> "dvr-tsout #117":0 []
	pad119: Source
		-> "demux-tsout #118":0 []
		-> "dvr-tsout #118":0 []
	pad120: Source
		-> "demux-tsout #119":0 []
		-> "dvr-tsout #119":0 []
	pad121: Source
		-> "demux-tsout #120":0 []
		-> "dvr-tsout #120":0 []
	pad122: Source
		-> "demux-tsout #121":0 []
		-> "dvr-tsout #121":0 []
	pad123: Source
		-> "demux-tsout #122":0 []
		-> "dvr-tsout #122":0 []
	pad124: Source
		-> "demux-tsout #123":0 []
		-> "dvr-tsout #123":0 []
	pad125: Source
		-> "demux-tsout #124":0 []
		-> "dvr-tsout #124":0 []
	pad126: Source
		-> "demux-tsout #125":0 []
		-> "dvr-tsout #125":0 []
	pad127: Source
		-> "demux-tsout #126":0 []
		-> "dvr-tsout #126":0 []
	pad128: Source
		-> "demux-tsout #127":0 []
		-> "dvr-tsout #127":0 []
	pad129: Source
		-> "demux-tsout #128":0 []
		-> "dvr-tsout #128":0 []
	pad130: Source
		-> "demux-tsout #129":0 []
		-> "dvr-tsout #129":0 []
	pad131: Source
		-> "demux-tsout #130":0 []
		-> "dvr-tsout #130":0 []
	pad132: Source
		-> "demux-tsout #131":0 []
		-> "dvr-tsout #131":0 []
	pad133: Source
		-> "demux-tsout #132":0 []
		-> "dvr-tsout #132":0 []
	pad134: Source
		-> "demux-tsout #133":0 []
		-> "dvr-tsout #133":0 []
	pad135: Source
		-> "demux-tsout #134":0 []
		-> "dvr-tsout #134":0 []
	pad136: Source
		-> "demux-tsout #135":0 []
		-> "dvr-tsout #135":0 []
	pad137: Source
		-> "demux-tsout #136":0 []
		-> "dvr-tsout #136":0 []
	pad138: Source
		-> "demux-tsout #137":0 []
		-> "dvr-tsout #137":0 []
	pad139: Source
		-> "demux-tsout #138":0 []
		-> "dvr-tsout #138":0 []
	pad140: Source
		-> "demux-tsout #139":0 []
		-> "dvr-tsout #139":0 []
	pad141: Source
		-> "demux-tsout #140":0 []
		-> "dvr-tsout #140":0 []
	pad142: Source
		-> "demux-tsout #141":0 []
		-> "dvr-tsout #141":0 []
	pad143: Source
		-> "demux-tsout #142":0 []
		-> "dvr-tsout #142":0 []
	pad144: Source
		-> "demux-tsout #143":0 []
		-> "dvr-tsout #143":0 []
	pad145: Source
		-> "demux-tsout #144":0 []
		-> "dvr-tsout #144":0 []
	pad146: Source
		-> "demux-tsout #145":0 []
		-> "dvr-tsout #145":0 []
	pad147: Source
		-> "demux-tsout #146":0 []
		-> "dvr-tsout #146":0 []
	pad148: Source
		-> "demux-tsout #147":0 []
		-> "dvr-tsout #147":0 []
	pad149: Source
		-> "demux-tsout #148":0 []
		-> "dvr-tsout #148":0 []
	pad150: Source
		-> "demux-tsout #149":0 []
		-> "dvr-tsout #149":0 []
	pad151: Source
		-> "demux-tsout #150":0 []
		-> "dvr-tsout #150":0 []
	pad152: Source
		-> "demux-tsout #151":0 []
		-> "dvr-tsout #151":0 []
	pad153: Source
		-> "demux-tsout #152":0 []
		-> "dvr-tsout #152":0 []
	pad154: Source
		-> "demux-tsout #153":0 []
		-> "dvr-tsout #153":0 []
	pad155: Source
		-> "demux-tsout #154":0 []
		-> "dvr-tsout #154":0 []
	pad156: Source
		-> "demux-tsout #155":0 []
		-> "dvr-tsout #155":0 []
	pad157: Source
		-> "demux-tsout #156":0 []
		-> "dvr-tsout #156":0 []
	pad158: Source
		-> "demux-tsout #157":0 []
		-> "dvr-tsout #157":0 []
	pad159: Source
		-> "demux-tsout #158":0 []
		-> "dvr-tsout #158":0 []
	pad160: Source
		-> "demux-tsout #159":0 []
		-> "dvr-tsout #159":0 []
	pad161: Source
		-> "demux-tsout #160":0 []
		-> "dvr-tsout #160":0 []
	pad162: Source
		-> "demux-tsout #161":0 []
		-> "dvr-tsout #161":0 []
	pad163: Source
		-> "demux-tsout #162":0 []
		-> "dvr-tsout #162":0 []
	pad164: Source
		-> "demux-tsout #163":0 []
		-> "dvr-tsout #163":0 []
	pad165: Source
		-> "demux-tsout #164":0 []
		-> "dvr-tsout #164":0 []
	pad166: Source
		-> "demux-tsout #165":0 []
		-> "dvr-tsout #165":0 []
	pad167: Source
		-> "demux-tsout #166":0 []
		-> "dvr-tsout #166":0 []
	pad168: Source
		-> "demux-tsout #167":0 []
		-> "dvr-tsout #167":0 []
	pad169: Source
		-> "demux-tsout #168":0 []
		-> "dvr-tsout #168":0 []
	pad170: Source
		-> "demux-tsout #169":0 []
		-> "dvr-tsout #169":0 []
	pad171: Source
		-> "demux-tsout #170":0 []
		-> "dvr-tsout #170":0 []
	pad172: Source
		-> "demux-tsout #171":0 []
		-> "dvr-tsout #171":0 []
	pad173: Source
		-> "demux-tsout #172":0 []
		-> "dvr-tsout #172":0 []
	pad174: Source
		-> "demux-tsout #173":0 []
		-> "dvr-tsout #173":0 []
	pad175: Source
		-> "demux-tsout #174":0 []
		-> "dvr-tsout #174":0 []
	pad176: Source
		-> "demux-tsout #175":0 []
		-> "dvr-tsout #175":0 []
	pad177: Source
		-> "demux-tsout #176":0 []
		-> "dvr-tsout #176":0 []
	pad178: Source
		-> "demux-tsout #177":0 []
		-> "dvr-tsout #177":0 []
	pad179: Source
		-> "demux-tsout #178":0 []
		-> "dvr-tsout #178":0 []
	pad180: Source
		-> "demux-tsout #179":0 []
		-> "dvr-tsout #179":0 []
	pad181: Source
		-> "demux-tsout #180":0 []
		-> "dvr-tsout #180":0 []
	pad182: Source
		-> "demux-tsout #181":0 []
		-> "dvr-tsout #181":0 []
	pad183: Source
		-> "demux-tsout #182":0 []
		-> "dvr-tsout #182":0 []
	pad184: Source
		-> "demux-tsout #183":0 []
		-> "dvr-tsout #183":0 []
	pad185: Source
		-> "demux-tsout #184":0 []
		-> "dvr-tsout #184":0 []
	pad186: Source
		-> "demux-tsout #185":0 []
		-> "dvr-tsout #185":0 []
	pad187: Source
		-> "demux-tsout #186":0 []
		-> "dvr-tsout #186":0 []
	pad188: Source
		-> "demux-tsout #187":0 []
		-> "dvr-tsout #187":0 []
	pad189: Source
		-> "demux-tsout #188":0 []
		-> "dvr-tsout #188":0 []
	pad190: Source
		-> "demux-tsout #189":0 []
		-> "dvr-tsout #189":0 []
	pad191: Source
		-> "demux-tsout #190":0 []
		-> "dvr-tsout #190":0 []
	pad192: Source
		-> "demux-tsout #191":0 []
		-> "dvr-tsout #191":0 []
	pad193: Source
		-> "demux-tsout #192":0 []
		-> "dvr-tsout #192":0 []
	pad194: Source
		-> "demux-tsout #193":0 []
		-> "dvr-tsout #193":0 []
	pad195: Source
		-> "demux-tsout #194":0 []
		-> "dvr-tsout #194":0 []
	pad196: Source
		-> "demux-tsout #195":0 []
		-> "dvr-tsout #195":0 []
	pad197: Source
		-> "demux-tsout #196":0 []
		-> "dvr-tsout #196":0 []
	pad198: Source
		-> "demux-tsout #197":0 []
		-> "dvr-tsout #197":0 []
	pad199: Source
		-> "demux-tsout #198":0 []
		-> "dvr-tsout #198":0 []
	pad200: Source
		-> "demux-tsout #199":0 []
		-> "dvr-tsout #199":0 []
	pad201: Source
		-> "demux-tsout #200":0 []
		-> "dvr-tsout #200":0 []
	pad202: Source
		-> "demux-tsout #201":0 []
		-> "dvr-tsout #201":0 []
	pad203: Source
		-> "demux-tsout #202":0 []
		-> "dvr-tsout #202":0 []
	pad204: Source
		-> "demux-tsout #203":0 []
		-> "dvr-tsout #203":0 []
	pad205: Source
		-> "demux-tsout #204":0 []
		-> "dvr-tsout #204":0 []
	pad206: Source
		-> "demux-tsout #205":0 []
		-> "dvr-tsout #205":0 []
	pad207: Source
		-> "demux-tsout #206":0 []
		-> "dvr-tsout #206":0 []
	pad208: Source
		-> "demux-tsout #207":0 []
		-> "dvr-tsout #207":0 []
	pad209: Source
		-> "demux-tsout #208":0 []
		-> "dvr-tsout #208":0 []
	pad210: Source
		-> "demux-tsout #209":0 []
		-> "dvr-tsout #209":0 []
	pad211: Source
		-> "demux-tsout #210":0 []
		-> "dvr-tsout #210":0 []
	pad212: Source
		-> "demux-tsout #211":0 []
		-> "dvr-tsout #211":0 []
	pad213: Source
		-> "demux-tsout #212":0 []
		-> "dvr-tsout #212":0 []
	pad214: Source
		-> "demux-tsout #213":0 []
		-> "dvr-tsout #213":0 []
	pad215: Source
		-> "demux-tsout #214":0 []
		-> "dvr-tsout #214":0 []
	pad216: Source
		-> "demux-tsout #215":0 []
		-> "dvr-tsout #215":0 []
	pad217: Source
		-> "demux-tsout #216":0 []
		-> "dvr-tsout #216":0 []
	pad218: Source
		-> "demux-tsout #217":0 []
		-> "dvr-tsout #217":0 []
	pad219: Source
		-> "demux-tsout #218":0 []
		-> "dvr-tsout #218":0 []
	pad220: Source
		-> "demux-tsout #219":0 []
		-> "dvr-tsout #219":0 []
	pad221: Source
		-> "demux-tsout #220":0 []
		-> "dvr-tsout #220":0 []
	pad222: Source
		-> "demux-tsout #221":0 []
		-> "dvr-tsout #221":0 []
	pad223: Source
		-> "demux-tsout #222":0 []
		-> "dvr-tsout #222":0 []
	pad224: Source
		-> "demux-tsout #223":0 []
		-> "dvr-tsout #223":0 []
	pad225: Source
		-> "demux-tsout #224":0 []
		-> "dvr-tsout #224":0 []
	pad226: Source
		-> "demux-tsout #225":0 []
		-> "dvr-tsout #225":0 []
	pad227: Source
		-> "demux-tsout #226":0 []
		-> "dvr-tsout #226":0 []
	pad228: Source
		-> "demux-tsout #227":0 []
		-> "dvr-tsout #227":0 []
	pad229: Source
		-> "demux-tsout #228":0 []
		-> "dvr-tsout #228":0 []
	pad230: Source
		-> "demux-tsout #229":0 []
		-> "dvr-tsout #229":0 []
	pad231: Source
		-> "demux-tsout #230":0 []
		-> "dvr-tsout #230":0 []
	pad232: Source
		-> "demux-tsout #231":0 []
		-> "dvr-tsout #231":0 []
	pad233: Source
		-> "demux-tsout #232":0 []
		-> "dvr-tsout #232":0 []
	pad234: Source
		-> "demux-tsout #233":0 []
		-> "dvr-tsout #233":0 []
	pad235: Source
		-> "demux-tsout #234":0 []
		-> "dvr-tsout #234":0 []
	pad236: Source
		-> "demux-tsout #235":0 []
		-> "dvr-tsout #235":0 []
	pad237: Source
		-> "demux-tsout #236":0 []
		-> "dvr-tsout #236":0 []
	pad238: Source
		-> "demux-tsout #237":0 []
		-> "dvr-tsout #237":0 []
	pad239: Source
		-> "demux-tsout #238":0 []
		-> "dvr-tsout #238":0 []
	pad240: Source
		-> "demux-tsout #239":0 []
		-> "dvr-tsout #239":0 []
	pad241: Source
		-> "demux-tsout #240":0 []
		-> "dvr-tsout #240":0 []
	pad242: Source
		-> "demux-tsout #241":0 []
		-> "dvr-tsout #241":0 []
	pad243: Source
		-> "demux-tsout #242":0 []
		-> "dvr-tsout #242":0 []
	pad244: Source
		-> "demux-tsout #243":0 []
		-> "dvr-tsout #243":0 []
	pad245: Source
		-> "demux-tsout #244":0 []
		-> "dvr-tsout #244":0 []
	pad246: Source
		-> "demux-tsout #245":0 []
		-> "dvr-tsout #245":0 []
	pad247: Source
		-> "demux-tsout #246":0 []
		-> "dvr-tsout #246":0 []
	pad248: Source
		-> "demux-tsout #247":0 []
		-> "dvr-tsout #247":0 []
	pad249: Source
		-> "demux-tsout #248":0 []
		-> "dvr-tsout #248":0 []
	pad250: Source
		-> "demux-tsout #249":0 []
		-> "dvr-tsout #249":0 []
	pad251: Source
		-> "demux-tsout #250":0 []
		-> "dvr-tsout #250":0 []
	pad252: Source
		-> "demux-tsout #251":0 []
		-> "dvr-tsout #251":0 []
	pad253: Source
		-> "demux-tsout #252":0 []
		-> "dvr-tsout #252":0 []
	pad254: Source
		-> "demux-tsout #253":0 []
		-> "dvr-tsout #253":0 []
	pad255: Source
		-> "demux-tsout #254":0 []
		-> "dvr-tsout #254":0 []
	pad256: Source
		-> "demux-tsout #255":0 []
		-> "dvr-tsout #255":0 []

- entity 812: dvr-tsout #0 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":1 []

- entity 814: dvr-tsout #1 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":2 []

- entity 816: dvr-tsout #2 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":3 []

- entity 818: dvr-tsout #3 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":4 []

- entity 820: dvr-tsout #4 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":5 []

- entity 822: dvr-tsout #5 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":6 []

- entity 824: dvr-tsout #6 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":7 []

- entity 826: dvr-tsout #7 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":8 []

- entity 828: dvr-tsout #8 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":9 []

- entity 830: dvr-tsout #9 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":10 []

- entity 832: dvr-tsout #10 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":11 []

- entity 834: dvr-tsout #11 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":12 []

- entity 836: dvr-tsout #12 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":13 []

- entity 838: dvr-tsout #13 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":14 []

- entity 840: dvr-tsout #14 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":15 []

- entity 842: dvr-tsout #15 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":16 []

- entity 844: dvr-tsout #16 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":17 []

- entity 846: dvr-tsout #17 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":18 []

- entity 848: dvr-tsout #18 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":19 []

- entity 850: dvr-tsout #19 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":20 []

- entity 852: dvr-tsout #20 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":21 []

- entity 854: dvr-tsout #21 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":22 []

- entity 856: dvr-tsout #22 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":23 []

- entity 858: dvr-tsout #23 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":24 []

- entity 860: dvr-tsout #24 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":25 []

- entity 862: dvr-tsout #25 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":26 []

- entity 864: dvr-tsout #26 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":27 []

- entity 866: dvr-tsout #27 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":28 []

- entity 868: dvr-tsout #28 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":29 []

- entity 870: dvr-tsout #29 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":30 []

- entity 872: dvr-tsout #30 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":31 []

- entity 874: dvr-tsout #31 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":32 []

- entity 876: dvr-tsout #32 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":33 []

- entity 878: dvr-tsout #33 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":34 []

- entity 880: dvr-tsout #34 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":35 []

- entity 882: dvr-tsout #35 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":36 []

- entity 884: dvr-tsout #36 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":37 []

- entity 886: dvr-tsout #37 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":38 []

- entity 888: dvr-tsout #38 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":39 []

- entity 890: dvr-tsout #39 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":40 []

- entity 892: dvr-tsout #40 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":41 []

- entity 894: dvr-tsout #41 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":42 []

- entity 896: dvr-tsout #42 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":43 []

- entity 898: dvr-tsout #43 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":44 []

- entity 900: dvr-tsout #44 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":45 []

- entity 902: dvr-tsout #45 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":46 []

- entity 904: dvr-tsout #46 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":47 []

- entity 906: dvr-tsout #47 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":48 []

- entity 908: dvr-tsout #48 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":49 []

- entity 910: dvr-tsout #49 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":50 []

- entity 912: dvr-tsout #50 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":51 []

- entity 914: dvr-tsout #51 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":52 []

- entity 916: dvr-tsout #52 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":53 []

- entity 918: dvr-tsout #53 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":54 []

- entity 920: dvr-tsout #54 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":55 []

- entity 922: dvr-tsout #55 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":56 []

- entity 924: dvr-tsout #56 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":57 []

- entity 926: dvr-tsout #57 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":58 []

- entity 928: dvr-tsout #58 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":59 []

- entity 930: dvr-tsout #59 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":60 []

- entity 932: dvr-tsout #60 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":61 []

- entity 934: dvr-tsout #61 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":62 []

- entity 936: dvr-tsout #62 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":63 []

- entity 938: dvr-tsout #63 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":64 []

- entity 940: dvr-tsout #64 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":65 []

- entity 942: dvr-tsout #65 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":66 []

- entity 944: dvr-tsout #66 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":67 []

- entity 946: dvr-tsout #67 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":68 []

- entity 948: dvr-tsout #68 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":69 []

- entity 950: dvr-tsout #69 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":70 []

- entity 952: dvr-tsout #70 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":71 []

- entity 954: dvr-tsout #71 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":72 []

- entity 956: dvr-tsout #72 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":73 []

- entity 958: dvr-tsout #73 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":74 []

- entity 960: dvr-tsout #74 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":75 []

- entity 962: dvr-tsout #75 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":76 []

- entity 964: dvr-tsout #76 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":77 []

- entity 966: dvr-tsout #77 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":78 []

- entity 968: dvr-tsout #78 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":79 []

- entity 970: dvr-tsout #79 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":80 []

- entity 972: dvr-tsout #80 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":81 []

- entity 974: dvr-tsout #81 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":82 []

- entity 976: dvr-tsout #82 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":83 []

- entity 978: dvr-tsout #83 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":84 []

- entity 980: dvr-tsout #84 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":85 []

- entity 982: dvr-tsout #85 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":86 []

- entity 984: dvr-tsout #86 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":87 []

- entity 986: dvr-tsout #87 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":88 []

- entity 988: dvr-tsout #88 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":89 []

- entity 990: dvr-tsout #89 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":90 []

- entity 992: dvr-tsout #90 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":91 []

- entity 994: dvr-tsout #91 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":92 []

- entity 996: dvr-tsout #92 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":93 []

- entity 998: dvr-tsout #93 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":94 []

- entity 1000: dvr-tsout #94 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":95 []

- entity 1002: dvr-tsout #95 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":96 []

- entity 1004: dvr-tsout #96 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":97 []

- entity 1006: dvr-tsout #97 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":98 []

- entity 1008: dvr-tsout #98 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":99 []

- entity 1010: dvr-tsout #99 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":100 []

- entity 1012: dvr-tsout #100 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":101 []

- entity 1014: dvr-tsout #101 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":102 []

- entity 1016: dvr-tsout #102 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":103 []

- entity 1018: dvr-tsout #103 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":104 []

- entity 1020: dvr-tsout #104 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":105 []

- entity 1022: dvr-tsout #105 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":106 []

- entity 1024: dvr-tsout #106 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":107 []

- entity 1026: dvr-tsout #107 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":108 []

- entity 1028: dvr-tsout #108 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":109 []

- entity 1030: dvr-tsout #109 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":110 []

- entity 1032: dvr-tsout #110 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":111 []

- entity 1034: dvr-tsout #111 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":112 []

- entity 1036: dvr-tsout #112 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":113 []

- entity 1038: dvr-tsout #113 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":114 []

- entity 1040: dvr-tsout #114 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":115 []

- entity 1042: dvr-tsout #115 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":116 []

- entity 1044: dvr-tsout #116 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":117 []

- entity 1046: dvr-tsout #117 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":118 []

- entity 1048: dvr-tsout #118 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":119 []

- entity 1050: dvr-tsout #119 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":120 []

- entity 1052: dvr-tsout #120 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":121 []

- entity 1054: dvr-tsout #121 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":122 []

- entity 1056: dvr-tsout #122 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":123 []

- entity 1058: dvr-tsout #123 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":124 []

- entity 1060: dvr-tsout #124 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":125 []

- entity 1062: dvr-tsout #125 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":126 []

- entity 1064: dvr-tsout #126 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":127 []

- entity 1066: dvr-tsout #127 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":128 []

- entity 1068: dvr-tsout #128 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":129 []

- entity 1070: dvr-tsout #129 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":130 []

- entity 1072: dvr-tsout #130 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":131 []

- entity 1074: dvr-tsout #131 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":132 []

- entity 1076: dvr-tsout #132 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":133 []

- entity 1078: dvr-tsout #133 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":134 []

- entity 1080: dvr-tsout #134 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":135 []

- entity 1082: dvr-tsout #135 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":136 []

- entity 1084: dvr-tsout #136 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":137 []

- entity 1086: dvr-tsout #137 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":138 []

- entity 1088: dvr-tsout #138 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":139 []

- entity 1090: dvr-tsout #139 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":140 []

- entity 1092: dvr-tsout #140 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":141 []

- entity 1094: dvr-tsout #141 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":142 []

- entity 1096: dvr-tsout #142 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":143 []

- entity 1098: dvr-tsout #143 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":144 []

- entity 1100: dvr-tsout #144 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":145 []

- entity 1102: dvr-tsout #145 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":146 []

- entity 1104: dvr-tsout #146 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":147 []

- entity 1106: dvr-tsout #147 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":148 []

- entity 1108: dvr-tsout #148 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":149 []

- entity 1110: dvr-tsout #149 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":150 []

- entity 1112: dvr-tsout #150 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":151 []

- entity 1114: dvr-tsout #151 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":152 []

- entity 1116: dvr-tsout #152 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":153 []

- entity 1118: dvr-tsout #153 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":154 []

- entity 1120: dvr-tsout #154 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":155 []

- entity 1122: dvr-tsout #155 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":156 []

- entity 1124: dvr-tsout #156 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":157 []

- entity 1126: dvr-tsout #157 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":158 []

- entity 1128: dvr-tsout #158 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":159 []

- entity 1130: dvr-tsout #159 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":160 []

- entity 1132: dvr-tsout #160 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":161 []

- entity 1134: dvr-tsout #161 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":162 []

- entity 1136: dvr-tsout #162 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":163 []

- entity 1138: dvr-tsout #163 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":164 []

- entity 1140: dvr-tsout #164 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":165 []

- entity 1142: dvr-tsout #165 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":166 []

- entity 1144: dvr-tsout #166 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":167 []

- entity 1146: dvr-tsout #167 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":168 []

- entity 1148: dvr-tsout #168 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":169 []

- entity 1150: dvr-tsout #169 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":170 []

- entity 1152: dvr-tsout #170 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":171 []

- entity 1154: dvr-tsout #171 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":172 []

- entity 1156: dvr-tsout #172 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":173 []

- entity 1158: dvr-tsout #173 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":174 []

- entity 1160: dvr-tsout #174 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":175 []

- entity 1162: dvr-tsout #175 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":176 []

- entity 1164: dvr-tsout #176 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":177 []

- entity 1166: dvr-tsout #177 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":178 []

- entity 1168: dvr-tsout #178 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":179 []

- entity 1170: dvr-tsout #179 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":180 []

- entity 1172: dvr-tsout #180 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":181 []

- entity 1174: dvr-tsout #181 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":182 []

- entity 1176: dvr-tsout #182 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":183 []

- entity 1178: dvr-tsout #183 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":184 []

- entity 1180: dvr-tsout #184 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":185 []

- entity 1182: dvr-tsout #185 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":186 []

- entity 1184: dvr-tsout #186 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":187 []

- entity 1186: dvr-tsout #187 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":188 []

- entity 1188: dvr-tsout #188 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":189 []

- entity 1190: dvr-tsout #189 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":190 []

- entity 1192: dvr-tsout #190 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":191 []

- entity 1194: dvr-tsout #191 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":192 []

- entity 1196: dvr-tsout #192 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":193 []

- entity 1198: dvr-tsout #193 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":194 []

- entity 1200: dvr-tsout #194 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":195 []

- entity 1202: dvr-tsout #195 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":196 []

- entity 1204: dvr-tsout #196 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":197 []

- entity 1206: dvr-tsout #197 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":198 []

- entity 1208: dvr-tsout #198 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":199 []

- entity 1210: dvr-tsout #199 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":200 []

- entity 1212: dvr-tsout #200 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":201 []

- entity 1214: dvr-tsout #201 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":202 []

- entity 1216: dvr-tsout #202 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":203 []

- entity 1218: dvr-tsout #203 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":204 []

- entity 1220: dvr-tsout #204 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":205 []

- entity 1222: dvr-tsout #205 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":206 []

- entity 1224: dvr-tsout #206 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":207 []

- entity 1226: dvr-tsout #207 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":208 []

- entity 1228: dvr-tsout #208 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":209 []

- entity 1230: dvr-tsout #209 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":210 []

- entity 1232: dvr-tsout #210 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":211 []

- entity 1234: dvr-tsout #211 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":212 []

- entity 1236: dvr-tsout #212 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":213 []

- entity 1238: dvr-tsout #213 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":214 []

- entity 1240: dvr-tsout #214 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":215 []

- entity 1242: dvr-tsout #215 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":216 []

- entity 1244: dvr-tsout #216 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":217 []

- entity 1246: dvr-tsout #217 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":218 []

- entity 1248: dvr-tsout #218 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":219 []

- entity 1250: dvr-tsout #219 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":220 []

- entity 1252: dvr-tsout #220 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":221 []

- entity 1254: dvr-tsout #221 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":222 []

- entity 1256: dvr-tsout #222 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":223 []

- entity 1258: dvr-tsout #223 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":224 []

- entity 1260: dvr-tsout #224 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":225 []

- entity 1262: dvr-tsout #225 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":226 []

- entity 1264: dvr-tsout #226 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":227 []

- entity 1266: dvr-tsout #227 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":228 []

- entity 1268: dvr-tsout #228 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":229 []

- entity 1270: dvr-tsout #229 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":230 []

- entity 1272: dvr-tsout #230 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":231 []

- entity 1274: dvr-tsout #231 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":232 []

- entity 1276: dvr-tsout #232 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":233 []

- entity 1278: dvr-tsout #233 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":234 []

- entity 1280: dvr-tsout #234 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":235 []

- entity 1282: dvr-tsout #235 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":236 []

- entity 1284: dvr-tsout #236 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":237 []

- entity 1286: dvr-tsout #237 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":238 []

- entity 1288: dvr-tsout #238 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":239 []

- entity 1290: dvr-tsout #239 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":240 []

- entity 1292: dvr-tsout #240 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":241 []

- entity 1294: dvr-tsout #241 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":242 []

- entity 1296: dvr-tsout #242 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":243 []

- entity 1298: dvr-tsout #243 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":244 []

- entity 1300: dvr-tsout #244 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":245 []

- entity 1302: dvr-tsout #245 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":246 []

- entity 1304: dvr-tsout #246 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":247 []

- entity 1306: dvr-tsout #247 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":248 []

- entity 1308: dvr-tsout #248 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":249 []

- entity 1310: dvr-tsout #249 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":250 []

- entity 1312: dvr-tsout #250 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":251 []

- entity 1314: dvr-tsout #251 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":252 []

- entity 1316: dvr-tsout #252 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":253 []

- entity 1318: dvr-tsout #253 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":254 []

- entity 1320: dvr-tsout #254 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":255 []

- entity 1322: dvr-tsout #255 (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "dvb-demux":256 []

- entity 2868: USB Mixer (3 pads, 2 links)

               type Node subtype Unknown flags 0
	pad0: Sink
		<- "au8522 13-0047":2 [ENABLED]
	pad1: Source
	pad2: Source
		-> "USB Audio":0 [ENABLED]

- entity 2875: USB Audio (1 pad, 1 link)
type Node subtype Unknown flags 0
pad0: Sink
<- "USB Mixer":2 [ENABLED]

#9 lore raw
In reply to Shuah Khan

Hi Shuah,

Quoted 12 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Quoted 9 lines

- This patch series is tested on 5.0-rc3 and addresses comments on v9 series from Hans Verkuil.
- v9 was tested on 4.20-rc6.
- Tested sharing resources with kaffeine, vlc, xawtv, tvtime, and arecord. When analog is streaming, digital and audio user-space applications detect that the tuner is busy and exit. When digital is streaming, analog and audio applications detect that the tuner is busy and exit. When arecord is owns the tuner, digital and analog detect that the tuner is busy and exit.

I've been doing some testing with my au0828, and I am confused about one thing, probably because it has been too long ago since I last looked into this in detail:

Why can't I change the tuner frequency if arecord (and only arecord) is streaming audio? If arecord is streaming, then it is recording the audio from the analog TV tuner, right? So changing the analog TV frequency should be fine.

I understand why you can't switch to DVB mode, but analog TV should be OK. Or am I missing something?

Regards,

Hans

Quoted 55 lines

- Tested media device allocator API with bind/unbind testing on snd-usb-audio and au0828 drivers to make sure /dev/mediaX is released only when the last driver is unbound.
- Addressed review comments from Hans on the RFC v8 (rebased on 4.19)
- Updated change log to describe the use-case more clearly.
- No changes to 0001,0002 code since the v7 referenced below.
- 0003 is a new patch to enable ALSA defines that have been disabled for kernel between 4.9 and 4.19.
- Minor merge conflict resolution in 0004.
- Added SPDX to new files.

Changes since v9:
- Patch 1: Fix mutex assert warning from find_module() calls. This code was written before the change to find_module() that requires callers to hold module_mutex. I missed this during my testing on 4.20-rc6. Hans Verkuil reported the problem.
- Patch 4: sound/usb: Initializes all the entities it can before registering the device based on comments from Hans Verkuil
- Carried Reviewed-by tag from Takashi Iwai for the sound from v9.
- No changes to Patches 2 and 3.

References:
https://lkml.org/lkml/2018/11/2/169
https://www.mail-archive.com/[email protected]/msg105854.html

Shuah Khan (4):
media: Media Device Allocator API
media: change au0828 to use Media Device Allocator API
media: media.h: Enable ALSA MEDIA_INTF_T* interface types sound/usb: Use Media Controller API to share media resources

 Documentation/media/kapi/mc-core.rst   |  41 ++++
 drivers/media/Makefile                 |   4 +
 drivers/media/media-dev-allocator.c    | 144 +++++++++++
 drivers/media/usb/au0828/au0828-core.c |  12 +-
 drivers/media/usb/au0828/au0828.h      |   1 +
 include/media/media-dev-allocator.h    |  53 ++++
 include/uapi/linux/media.h             |  25 +-
 sound/usb/Kconfig                      |   4 +
 sound/usb/Makefile                     |   2 +
 sound/usb/card.c                       |  14 ++
 sound/usb/card.h                       |   3 +
 sound/usb/media.c                      | 327 +++++++++++++++++++++++++
 sound/usb/media.h                      |  74 ++++++
 sound/usb/mixer.h                      |   3 +
 sound/usb/pcm.c                        |  29 ++-
 sound/usb/quirks-table.h               |   1 +
 sound/usb/stream.c                     |   2 +
 sound/usb/usbaudio.h                   |   6 +
 18 files changed, 723 insertions(+), 22 deletions(-)
 create mode 100644 drivers/media/media-dev-allocator.c
 create mode 100644 include/media/media-dev-allocator.h
 create mode 100644 sound/usb/media.c
 create mode 100644 sound/usb/media.h
author
#10 lore raw

Hi Hans,

Quoted 25 lines · Hans Verkuil

Hi Shuah,

Quoted 12 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Quoted 9 lines

- This patch series is tested on 5.0-rc3 and addresses comments on v9 series from Hans Verkuil.
- v9 was tested on 4.20-rc6.
- Tested sharing resources with kaffeine, vlc, xawtv, tvtime, and arecord. When analog is streaming, digital and audio user-space applications detect that the tuner is busy and exit. When digital is streaming, analog and audio applications detect that the tuner is busy and exit. When arecord is owns the tuner, digital and analog detect that the tuner is busy and exit.

Quoted 4 lines

I've been doing some testing with my au0828, and I am confused about one thing, probably because it has been too long ago since I last looked into this in detail:

Great.

Quoted 5 lines

Why can't I change the tuner frequency if arecord (and only arecord) is streaming audio? If arecord is streaming, then it is recording the audio from the analog TV tuner, right? So changing the analog TV frequency should be fine.

Changing analog TV frequency would be s_frequency. The way it works is any s_* calls would require holding the pipeline. In Analog TV case, it would mean holding both audio and video pipelines for any changes including TV.

As I recall, we discussed this design and the decision was to make all s_* calls interfaces to hold the tuner. A special exception is g_tuner in case of au0828. au0828 initializes the tuner from s_* interfaces and its g_tuner interfaces. Allowing s_frequency to proceed will disrupt the arecord audio stream.

Query (q_*) works just fine without holding the pipeline. I limited the analog holds to just the ones that are required. The current set is required to avoid audio stream disruptions.

I made sure v4l-ctl --all works when the pipeline is locked by any one of the 3 (audio, video, DVB).

Hope this helps.

thanks,
-- Shuah

#11 lore raw
Quoted 52 lines · shuah

Hi Hans,

Quoted 25 lines · Hans Verkuil

Hi Shuah,

Quoted 12 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Quoted 9 lines

- This patch series is tested on 5.0-rc3 and addresses comments on v9 series from Hans Verkuil.
- v9 was tested on 4.20-rc6.
- Tested sharing resources with kaffeine, vlc, xawtv, tvtime, and arecord. When analog is streaming, digital and audio user-space applications detect that the tuner is busy and exit. When digital is streaming, analog and audio applications detect that the tuner is busy and exit. When arecord is owns the tuner, digital and analog detect that the tuner is busy and exit.

Quoted 4 lines

I've been doing some testing with my au0828, and I am confused about one thing, probably because it has been too long ago since I last looked into this in detail:

Great.

Quoted 5 lines

Why can't I change the tuner frequency if arecord (and only arecord) is streaming audio? If arecord is streaming, then it is recording the audio from the analog TV tuner, right? So changing the analog TV frequency should be fine.

Changing analog TV frequency would be s_frequency. The way it works is any s_* calls would require holding the pipeline. In Analog TV case, it would mean holding both audio and video pipelines for any changes including TV.

As I recall, we discussed this design and the decision was to make all s_* calls interfaces to hold the tuner. A special exception is g_tuner in case of au0828. au0828 initializes the tuner from s_* interfaces and its g_tuner interfaces. Allowing s_frequency to proceed will disrupt the arecord audio stream.

Quoted 3 lines

Query (q_*) works just fine without holding the pipeline. I limited the analog holds to just the ones that are required. The current set is required to avoid audio stream disruptions.

So I am not sure about that ('avoid audio stream disruptions'): if I stream video AND use arecord, then I can just set the frequency while streaming. Doesn't that interrupt audio as well? And are you sure changing the tuner frequency actually disrupts audio? And if audio is disrupted, are we talking about a glitch or is audio permanently disrupted?

That's basically the inconsistent behavior I noticed: just running arecord will prevent me from changing the frequency, but if I run arecord and stream video, then it is suddenly OK to change the frequency.

BTW, I think there was also inconsistent behavior in the order of streaming audio and video: if I stream video first, then I can stream audio afterwards. But if I stream audio first, then (if I remember correctly) I can't start video streaming.

Regards,

Hans

Quoted 10 lines

I made sure v4l-ctl --all works when the pipeline is locked by any one of the 3 (audio, video, DVB).

Hope this helps.

thanks,
-- Shuah

author
#12 lore raw
Quoted 57 lines · Hans Verkuil
Quoted 52 lines · shuah

Hi Hans,

Quoted 25 lines · Hans Verkuil

Hi Shuah,

Quoted 12 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Quoted 9 lines

- This patch series is tested on 5.0-rc3 and addresses comments on v9 series from Hans Verkuil.
- v9 was tested on 4.20-rc6.
- Tested sharing resources with kaffeine, vlc, xawtv, tvtime, and arecord. When analog is streaming, digital and audio user-space applications detect that the tuner is busy and exit. When digital is streaming, analog and audio applications detect that the tuner is busy and exit. When arecord is owns the tuner, digital and analog detect that the tuner is busy and exit.

Quoted 4 lines

I've been doing some testing with my au0828, and I am confused about one thing, probably because it has been too long ago since I last looked into this in detail:

Great.

Quoted 5 lines

Why can't I change the tuner frequency if arecord (and only arecord) is streaming audio? If arecord is streaming, then it is recording the audio from the analog TV tuner, right? So changing the analog TV frequency should be fine.

Changing analog TV frequency would be s_frequency. The way it works is any s_* calls would require holding the pipeline. In Analog TV case, it would mean holding both audio and video pipelines for any changes including TV.

As I recall, we discussed this design and the decision was to make all s_* calls interfaces to hold the tuner. A special exception is g_tuner in case of au0828. au0828 initializes the tuner from s_* interfaces and its g_tuner interfaces. Allowing s_frequency to proceed will disrupt the arecord audio stream.

Quoted 3 lines

Query (q_*) works just fine without holding the pipeline. I limited the analog holds to just the ones that are required. The current set is required to avoid audio stream disruptions.

Quoted 5 lines

So I am not sure about that ('avoid audio stream disruptions'): if I stream video AND use arecord, then I can just set the frequency while streaming. Doesn't that interrupt audio as well? And are you sure changing the tuner frequency actually disrupts audio? And if audio is disrupted, are we talking about a glitch or is audio permanently disrupted?

I think it is a glitch. I will run some tests and let you know.

Quoted 4 lines

That's basically the inconsistent behavior I noticed: just running arecord will prevent me from changing the frequency, but if I run arecord and stream video, then it is suddenly OK to change the frequency.

How are you changing frequency? I want to duplicate what you are doing.

Quoted 6 lines

BTW, I think there was also inconsistent behavior in the order of streaming audio and video: if I stream video first, then I can stream audio afterwards. But if I stream audio first, then (if I remember correctly) I can't start video streaming.

I will run some tests tomorrow and see what I find. Which video apps are you running for these tests?

thanks,
-- Shuah

#13 lore raw
Quoted 64 lines · shuah
Quoted 57 lines · Hans Verkuil
Quoted 52 lines · shuah

Hi Hans,

Quoted 25 lines · Hans Verkuil

Hi Shuah,

Quoted 12 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Quoted 9 lines

- This patch series is tested on 5.0-rc3 and addresses comments on v9 series from Hans Verkuil.
- v9 was tested on 4.20-rc6.
- Tested sharing resources with kaffeine, vlc, xawtv, tvtime, and arecord. When analog is streaming, digital and audio user-space applications detect that the tuner is busy and exit. When digital is streaming, analog and audio applications detect that the tuner is busy and exit. When arecord is owns the tuner, digital and analog detect that the tuner is busy and exit.

Quoted 4 lines

I've been doing some testing with my au0828, and I am confused about one thing, probably because it has been too long ago since I last looked into this in detail:

Great.

Quoted 5 lines

Why can't I change the tuner frequency if arecord (and only arecord) is streaming audio? If arecord is streaming, then it is recording the audio from the analog TV tuner, right? So changing the analog TV frequency should be fine.

Changing analog TV frequency would be s_frequency. The way it works is any s_* calls would require holding the pipeline. In Analog TV case, it would mean holding both audio and video pipelines for any changes including TV.

As I recall, we discussed this design and the decision was to make all s_* calls interfaces to hold the tuner. A special exception is g_tuner in case of au0828. au0828 initializes the tuner from s_* interfaces and its g_tuner interfaces. Allowing s_frequency to proceed will disrupt the arecord audio stream.

Quoted 3 lines

Query (q_*) works just fine without holding the pipeline. I limited the analog holds to just the ones that are required. The current set is required to avoid audio stream disruptions.

Quoted 5 lines

So I am not sure about that ('avoid audio stream disruptions'): if I stream video AND use arecord, then I can just set the frequency while streaming. Doesn't that interrupt audio as well? And are you sure changing the tuner frequency actually disrupts audio? And if audio is disrupted, are we talking about a glitch or is audio permanently disrupted?

Quoted 7 lines

I think it is a glitch. I will run some tests and let you know.

Quoted 4 lines

That's basically the inconsistent behavior I noticed: just running arecord will prevent me from changing the frequency, but if I run arecord and stream video, then it is suddenly OK to change the frequency.

How are you changing frequency? I want to duplicate what you are doing.

v4l2-ctl -f <freq>

Quoted 10 lines
Quoted 7 lines

BTW, I think there was also inconsistent behavior in the order of streaming audio and video: if I stream video first, then I can stream audio afterwards. But if I stream audio first, then (if I remember correctly) I can't start video streaming.

I will run some tests tomorrow and see what I find. Which video apps are you running for these tests?

v4l2-ctl or qv4l2.

Regards,

Hans

Quoted 4 lines

thanks,
-- Shuah

author
#14 lore raw

Hi Hans,

Quoted 67 lines · Hans Verkuil
Quoted 66 lines · shuah
Quoted 57 lines · Hans Verkuil
Quoted 52 lines · shuah

Hi Hans,

Quoted 25 lines · Hans Verkuil

Hi Shuah,

Quoted 12 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Quoted 9 lines

- This patch series is tested on 5.0-rc3 and addresses comments on v9 series from Hans Verkuil.
- v9 was tested on 4.20-rc6.
- Tested sharing resources with kaffeine, vlc, xawtv, tvtime, and arecord. When analog is streaming, digital and audio user-space applications detect that the tuner is busy and exit. When digital is streaming, analog and audio applications detect that the tuner is busy and exit. When arecord is owns the tuner, digital and analog detect that the tuner is busy and exit.

Quoted 4 lines

I've been doing some testing with my au0828, and I am confused about one thing, probably because it has been too long ago since I last looked into this in detail:

Great.

Quoted 5 lines

Why can't I change the tuner frequency if arecord (and only arecord) is streaming audio? If arecord is streaming, then it is recording the audio from the analog TV tuner, right? So changing the analog TV frequency should be fine.

Changing analog TV frequency would be s_frequency. The way it works is any s_* calls would require holding the pipeline. In Analog TV case, it would mean holding both audio and video pipelines for any changes including TV.

As I recall, we discussed this design and the decision was to make all s_* calls interfaces to hold the tuner. A special exception is g_tuner in case of au0828. au0828 initializes the tuner from s_* interfaces and its g_tuner interfaces. Allowing s_frequency to proceed will disrupt the arecord audio stream.

Quoted 3 lines

Query (q_*) works just fine without holding the pipeline. I limited the analog holds to just the ones that are required. The current set is required to avoid audio stream disruptions.

Quoted 5 lines

So I am not sure about that ('avoid audio stream disruptions'): if I stream video AND use arecord, then I can just set the frequency while streaming. Doesn't that interrupt audio as well? And are you sure changing the tuner frequency actually disrupts audio? And if audio is disrupted, are we talking about a glitch or is audio permanently disrupted?

I think it is a glitch. I will run some tests and let you know.

Quoted 1 line
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines

That's basically the inconsistent behavior I noticed: just running arecord will prevent me from changing the frequency, but if I run arecord and stream video, then it is suddenly OK to change the frequency.

How are you changing frequency? I want to duplicate what you are doing.

v4l2-ctl -f <freq>

I am not seeing the inconsistent behavior. Here are my results.

1. Started acecord and while it is running:

arecord -M -D plughw:2,0 -c2 -f S16_LE -t wav foo.wav
Recording WAVE 'foo.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo

2. Ran v4l2-ctl -f as follows:

v4l2-ctl -f 700
VIDIOC_G_TUNER: failed: Device or resource busy
VIDIOC_S_FREQUENCY: failed: Device or resource busy

Based on the current implementation, it failed with resource busy as expected.

3. Started v4l2-ctl as follows:

v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0
VIDIOC_STREAMON: failed: Device or resource busy

shuah@deneb:/mnt/data/lkml/v4l-utils/utils/v4l2-ctl$ v4l2-ctl -f 700
VIDIOC_G_TUNER: failed: Device or resource busy
VIDIOC_S_FREQUENCY: failed: Device or resource busy

Based on the current implementation, it failed with resource busy as expected.

4. After stopping arecord:

v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0 <<<<<<<<<<<<< 11.88 fps
<<<<<<<<<<<<<<< 13.36 fps
<<<<<<<<<<<<<<< 14.00 fps
<<<<<<<<<<<<<<<< 14.35 fps
<<<<<<<<<<<<<<< 14.57 fps
<<<<<<<<<<<<<<<< 14.71 fps
<<<<<<<<<

Worked as expected.

5. After stopping above video streaming:

arecord -M -D plughw:2,0 -c2 -f S16_LE -t wav foo.wav
Recording WAVE 'foo.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo

Worked as expected.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

BTW, I think there was also inconsistent behavior in the order of streaming audio and video: if I stream video first, then I can stream audio afterwards. But if I stream audio first, then (if I remember correctly) I can't start video streaming.

Okay this is what I saw:

While v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0

I could start arecord -M -D plughw:2,0 -c2 -f S16_LE -t wav foo.wav

I ran strace on v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0 and I didn't see AUDIO holds. The only think of here is that

v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0

doesn't open audio device. I didn't see this when tested with other video apps, (tvtine, xawtv, vlc). When video is streaming, I see arecord failing with device busy.

As far I can see, exclusion logic is working correctly. s_freq does fail now based on the current logic.

btw I tested all of this on 5.0-rc4

thanks,
-- Shuah

#15 lore raw
Quoted 91 lines · shuah

Hi Hans,

Quoted 67 lines · Hans Verkuil
Quoted 66 lines · shuah
Quoted 57 lines · Hans Verkuil
Quoted 52 lines · shuah

Hi Hans,

Quoted 25 lines · Hans Verkuil

Hi Shuah,

Quoted 12 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Quoted 9 lines

- This patch series is tested on 5.0-rc3 and addresses comments on v9 series from Hans Verkuil.
- v9 was tested on 4.20-rc6.
- Tested sharing resources with kaffeine, vlc, xawtv, tvtime, and arecord. When analog is streaming, digital and audio user-space applications detect that the tuner is busy and exit. When digital is streaming, analog and audio applications detect that the tuner is busy and exit. When arecord is owns the tuner, digital and analog detect that the tuner is busy and exit.

Quoted 4 lines

I've been doing some testing with my au0828, and I am confused about one thing, probably because it has been too long ago since I last looked into this in detail:

Great.

Quoted 5 lines

Why can't I change the tuner frequency if arecord (and only arecord) is streaming audio? If arecord is streaming, then it is recording the audio from the analog TV tuner, right? So changing the analog TV frequency should be fine.

Changing analog TV frequency would be s_frequency. The way it works is any s_* calls would require holding the pipeline. In Analog TV case, it would mean holding both audio and video pipelines for any changes including TV.

As I recall, we discussed this design and the decision was to make all s_* calls interfaces to hold the tuner. A special exception is g_tuner in case of au0828. au0828 initializes the tuner from s_* interfaces and its g_tuner interfaces. Allowing s_frequency to proceed will disrupt the arecord audio stream.

Quoted 3 lines

Query (q_*) works just fine without holding the pipeline. I limited the analog holds to just the ones that are required. The current set is required to avoid audio stream disruptions.

Quoted 5 lines

So I am not sure about that ('avoid audio stream disruptions'): if I stream video AND use arecord, then I can just set the frequency while streaming. Doesn't that interrupt audio as well? And are you sure changing the tuner frequency actually disrupts audio? And if audio is disrupted, are we talking about a glitch or is audio permanently disrupted?

I think it is a glitch. I will run some tests and let you know.

Quoted 1 line
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines

That's basically the inconsistent behavior I noticed: just running arecord will prevent me from changing the frequency, but if I run arecord and stream video, then it is suddenly OK to change the frequency.

How are you changing frequency? I want to duplicate what you are doing.

v4l2-ctl -f <freq>

I am not seeing the inconsistent behavior. Here are my results.

1. Started acecord and while it is running:

arecord -M -D plughw:2,0 -c2  -f S16_LE -t wav foo.wav
Recording WAVE 'foo.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo

2. Ran v4l2-ctl -f as follows:

v4l2-ctl -f 700
VIDIOC_G_TUNER: failed: Device or resource busy
VIDIOC_S_FREQUENCY: failed: Device or resource busy

Quoted 7 lines

Based on the current implementation, it failed with resource busy as expected.

3. Started v4l2-ctl as follows:

 v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0
VIDIOC_STREAMON: failed: Device or resource busy

Why is this? You have one analog tuner and it delivers independent audio and video streams. I should be able to start/stop audio and video independently.

And as mentioned above, if I use v4l2-ctl for video streaming, then start arecord, then that works fine. And I can change the tuner frequency while both are streaming.

But doing this the other way around (first start arecord, then v4l2-ctl) then that doesn't work.

It makes no sense.

Note that v4l2-ctl does not open audio, it solely deals with video. qv4l2 will open both audio and video, but for testing audio and video independently you need to use arecord/v4l2-ctl.

In any case, my understanding of how this should work is that both arecord and v4l2-ctl should attempt to lock the analog tuner resource, but they can share it.

If DVB is using the tuner, then both arecord and v4l2-ctl should fail with -EBUSY. Same if either of arecord/v4l2-ctl is running, then DVB should fail with -EBUSY.

BTW, I won't be able to test anything myself until Feb 9th since I'm abroad and don't have access to my au0828.

Regards,

Hans

Quoted 61 lines
shuah@deneb:/mnt/data/lkml/v4l-utils/utils/v4l2-ctl$ v4l2-ctl -f 700
VIDIOC_G_TUNER: failed: Device or resource busy
VIDIOC_S_FREQUENCY: failed: Device or resource busy

Based on the current implementation, it failed with resource busy as expected.

4. After stopping arecord:

v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0 <<<<<<<<<<<<< 11.88 fps
<<<<<<<<<<<<<<< 13.36 fps
<<<<<<<<<<<<<<< 14.00 fps
<<<<<<<<<<<<<<<< 14.35 fps
<<<<<<<<<<<<<<< 14.57 fps
<<<<<<<<<<<<<<<< 14.71 fps
<<<<<<<<<

Worked as expected.

5. After stopping above video streaming:

arecord -M -D plughw:2,0 -c2  -f S16_LE -t wav foo.wav
Recording WAVE 'foo.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo

Worked as expected.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

BTW, I think there was also inconsistent behavior in the order of streaming audio and video: if I stream video first, then I can stream audio afterwards. But if I stream audio first, then (if I remember correctly) I can't start video streaming.

Okay this is what I saw:

While v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0

I could start arecord -M -D plughw:2,0 -c2  -f S16_LE -t wav foo.wav

I ran strace on v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0 and I didn't see AUDIO holds. The only think of here is that

v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0

doesn't open audio device. I didn't see this when tested with other video apps, (tvtine, xawtv, vlc). When video is streaming, I see arecord failing with device busy.

As far I can see, exclusion logic is working correctly. s_freq does fail now based on the current logic.

btw I tested all of this on 5.0-rc4

thanks,
-- Shuah

author
#16 lore raw
Quoted 115 lines · Hans Verkuil
Quoted 91 lines · shuah

Hi Hans,

Quoted 67 lines · Hans Verkuil
Quoted 66 lines · shuah
Quoted 57 lines · Hans Verkuil
Quoted 52 lines · shuah

Hi Hans,

Quoted 25 lines · Hans Verkuil

Hi Shuah,

Quoted 12 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Quoted 9 lines

- This patch series is tested on 5.0-rc3 and addresses comments on v9 series from Hans Verkuil.
- v9 was tested on 4.20-rc6.
- Tested sharing resources with kaffeine, vlc, xawtv, tvtime, and arecord. When analog is streaming, digital and audio user-space applications detect that the tuner is busy and exit. When digital is streaming, analog and audio applications detect that the tuner is busy and exit. When arecord is owns the tuner, digital and analog detect that the tuner is busy and exit.

Quoted 4 lines

I've been doing some testing with my au0828, and I am confused about one thing, probably because it has been too long ago since I last looked into this in detail:

Great.

Quoted 5 lines

Why can't I change the tuner frequency if arecord (and only arecord) is streaming audio? If arecord is streaming, then it is recording the audio from the analog TV tuner, right? So changing the analog TV frequency should be fine.

Changing analog TV frequency would be s_frequency. The way it works is any s_* calls would require holding the pipeline. In Analog TV case, it would mean holding both audio and video pipelines for any changes including TV.

As I recall, we discussed this design and the decision was to make all s_* calls interfaces to hold the tuner. A special exception is g_tuner in case of au0828. au0828 initializes the tuner from s_* interfaces and its g_tuner interfaces. Allowing s_frequency to proceed will disrupt the arecord audio stream.

Quoted 3 lines

Query (q_*) works just fine without holding the pipeline. I limited the analog holds to just the ones that are required. The current set is required to avoid audio stream disruptions.

Quoted 5 lines

So I am not sure about that ('avoid audio stream disruptions'): if I stream video AND use arecord, then I can just set the frequency while streaming. Doesn't that interrupt audio as well? And are you sure changing the tuner frequency actually disrupts audio? And if audio is disrupted, are we talking about a glitch or is audio permanently disrupted?

I think it is a glitch. I will run some tests and let you know.

Quoted 1 line
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines

That's basically the inconsistent behavior I noticed: just running arecord will prevent me from changing the frequency, but if I run arecord and stream video, then it is suddenly OK to change the frequency.

How are you changing frequency? I want to duplicate what you are doing.

v4l2-ctl -f <freq>

I am not seeing the inconsistent behavior. Here are my results.

1. Started acecord and while it is running:

arecord -M -D plughw:2,0 -c2  -f S16_LE -t wav foo.wav
Recording WAVE 'foo.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo

2. Ran v4l2-ctl -f as follows:

v4l2-ctl -f 700
VIDIOC_G_TUNER: failed: Device or resource busy
VIDIOC_S_FREQUENCY: failed: Device or resource busy

Quoted 7 lines

Based on the current implementation, it failed with resource busy as expected.

3. Started v4l2-ctl as follows:

 v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0
VIDIOC_STREAMON: failed: Device or resource busy

Why is this? You have one analog tuner and it delivers independent audio and video streams. I should be able to start/stop audio and video independently.

And as mentioned above, if I use v4l2-ctl for video streaming, then start arecord, then that works fine. And I can change the tuner frequency while both are streaming.

But doing this the other way around (first start arecord, then v4l2-ctl) then that doesn't work.

It makes no sense.

Note that v4l2-ctl does not open audio, it solely deals with video. qv4l2 will open both audio and video, but for testing audio and video independently you need to use arecord/v4l2-ctl.

Quoted 9 lines

In any case, my understanding of how this should work is that both arecord and v4l2-ctl should attempt to lock the analog tuner resource, but they can share it.

If DVB is using the tuner, then both arecord and v4l2-ctl should fail with -EBUSY. Same if either of arecord/v4l2-ctl is running, then DVB should fail with -EBUSY.

BTW, I won't be able to test anything myself until Feb 9th since I'm abroad and don't have access to my au0828.

I am also on a business trip at the moment and won't have access to my au0828 until Feb 8th.

I think I understand your concern and the change to get the desired behavior of allowing tuner to be shared by arecord and v4l2-ctl is going to in the code that is already in the mainline which is the
au0828_enable_source().

If you would like to wait to pull this series for the problem to be fixed, I can send that change in. It will be another patch added to the series.

Or if you pull this and I can send fix on top. Let me know your preference.

thanks,
-- Shuah

#17 lore raw
Quoted 129 lines · shuah
Quoted 115 lines · Hans Verkuil
Quoted 91 lines · shuah

Hi Hans,

Quoted 67 lines · Hans Verkuil
Quoted 66 lines · shuah
Quoted 57 lines · Hans Verkuil
Quoted 52 lines · shuah

Hi Hans,

Quoted 25 lines · Hans Verkuil

Hi Shuah,

Quoted 12 lines · Shuah Khan

Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource.

Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released.

Quoted 9 lines

- This patch series is tested on 5.0-rc3 and addresses comments on v9 series from Hans Verkuil.
- v9 was tested on 4.20-rc6.
- Tested sharing resources with kaffeine, vlc, xawtv, tvtime, and arecord. When analog is streaming, digital and audio user-space applications detect that the tuner is busy and exit. When digital is streaming, analog and audio applications detect that the tuner is busy and exit. When arecord is owns the tuner, digital and analog detect that the tuner is busy and exit.

Quoted 4 lines

I've been doing some testing with my au0828, and I am confused about one thing, probably because it has been too long ago since I last looked into this in detail:

Great.

Quoted 5 lines

Why can't I change the tuner frequency if arecord (and only arecord) is streaming audio? If arecord is streaming, then it is recording the audio from the analog TV tuner, right? So changing the analog TV frequency should be fine.

Changing analog TV frequency would be s_frequency. The way it works is any s_* calls would require holding the pipeline. In Analog TV case, it would mean holding both audio and video pipelines for any changes including TV.

As I recall, we discussed this design and the decision was to make all s_* calls interfaces to hold the tuner. A special exception is g_tuner in case of au0828. au0828 initializes the tuner from s_* interfaces and its g_tuner interfaces. Allowing s_frequency to proceed will disrupt the arecord audio stream.

Quoted 3 lines

Query (q_*) works just fine without holding the pipeline. I limited the analog holds to just the ones that are required. The current set is required to avoid audio stream disruptions.

Quoted 5 lines

So I am not sure about that ('avoid audio stream disruptions'): if I stream video AND use arecord, then I can just set the frequency while streaming. Doesn't that interrupt audio as well? And are you sure changing the tuner frequency actually disrupts audio? And if audio is disrupted, are we talking about a glitch or is audio permanently disrupted?

I think it is a glitch. I will run some tests and let you know.

Quoted 1 line
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines

That's basically the inconsistent behavior I noticed: just running arecord will prevent me from changing the frequency, but if I run arecord and stream video, then it is suddenly OK to change the frequency.

How are you changing frequency? I want to duplicate what you are doing.

v4l2-ctl -f <freq>

I am not seeing the inconsistent behavior. Here are my results.

1. Started acecord and while it is running:

arecord -M -D plughw:2,0 -c2  -f S16_LE -t wav foo.wav
Recording WAVE 'foo.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo

2. Ran v4l2-ctl -f as follows:

v4l2-ctl -f 700
VIDIOC_G_TUNER: failed: Device or resource busy
VIDIOC_S_FREQUENCY: failed: Device or resource busy

Quoted 7 lines

Based on the current implementation, it failed with resource busy as expected.

3. Started v4l2-ctl as follows:

  v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video0
VIDIOC_STREAMON: failed: Device or resource busy

Why is this? You have one analog tuner and it delivers independent audio and video streams. I should be able to start/stop audio and video independently.

And as mentioned above, if I use v4l2-ctl for video streaming, then start arecord, then that works fine. And I can change the tuner frequency while both are streaming.

But doing this the other way around (first start arecord, then v4l2-ctl) then that doesn't work.

It makes no sense.

Note that v4l2-ctl does not open audio, it solely deals with video. qv4l2 will open both audio and video, but for testing audio and video independently you need to use arecord/v4l2-ctl.

Quoted 9 lines

In any case, my understanding of how this should work is that both arecord and v4l2-ctl should attempt to lock the analog tuner resource, but they can share it.

If DVB is using the tuner, then both arecord and v4l2-ctl should fail with -EBUSY. Same if either of arecord/v4l2-ctl is running, then DVB should fail with -EBUSY.

BTW, I won't be able to test anything myself until Feb 9th since I'm abroad and don't have access to my au0828.

I am also on a business trip at the moment and won't have access to my au0828 until Feb 8th.

Quoted 8 lines

I think I understand your concern and the change to get the desired behavior of allowing tuner to be shared by arecord and v4l2-ctl is going to in the code that is already in the mainline which is the
au0828_enable_source().

If you would like to wait to pull this series for the problem to be fixed, I can send that change in. It will be another patch added to the series.

Or if you pull this and I can send fix on top. Let me know your preference.

I prefer to wait until I can test with the additional patch if that's OK?

Regards,

Hans