summaryrefslogtreecommitdiffstats
path: root/sys/dvb
diff options
context:
space:
mode:
authorAlessandro Decina <alessandro@nnva.org>2007-10-16 17:38:05 +0000
committerZaheer Abbas Merali <zaheerabbas@merali.org>2007-10-16 17:38:05 +0000
commitc205b740d4e793354493c4a2663e45670255453c (patch)
tree3c5d8978e68cf860b69c279e14372ccc144a883c /sys/dvb
parent389904f6248c65468175c072441747f51c5a5296 (diff)
downloadgst-plugins-bad-c205b740d4e793354493c4a2663e45670255453c.tar.gz
gst-plugins-bad-c205b740d4e793354493c4a2663e45670255453c.tar.bz2
gst-plugins-bad-c205b740d4e793354493c4a2663e45670255453c.zip
sys/dvb/: Integrate SoC work done by Alessandro for the Freevo project.
Original commit message from CVS: patch by: Alessandro Decina * sys/dvb/Makefile.am: * sys/dvb/cam.c: * sys/dvb/cam.h: * sys/dvb/camapplication.c: * sys/dvb/camapplication.h: * sys/dvb/camapplicationinfo.c: * sys/dvb/camapplicationinfo.h: * sys/dvb/camconditionalaccess.c: * sys/dvb/camconditionalaccess.h: * sys/dvb/camdevice.c: * sys/dvb/camdevice.h: * sys/dvb/camresourcemanager.c: * sys/dvb/camresourcemanager.h: * sys/dvb/camsession.c: * sys/dvb/camsession.h: * sys/dvb/camswclient.c: * sys/dvb/camswclient.h: * sys/dvb/camtransport.c: * sys/dvb/camtransport.h: * sys/dvb/camutils.c: * sys/dvb/camutils.h: * sys/dvb/dvbbasebin.c: * sys/dvb/dvbbasebin.h: * sys/dvb/gstdvb.c: * sys/dvb/gstdvbsrc.c: * sys/dvb/gstdvbsrc.h: Integrate SoC work done by Alessandro for the Freevo project. Adds cam support to the dvb stack in GStreamer and a new element (actually a bin) called dvbbasebin that integrates dvbsrc and mpegtsparse to a) handle decryption and b) allow acquiring multiple channels on same transponder without knowing pid numbers.
Diffstat (limited to 'sys/dvb')
-rw-r--r--sys/dvb/Makefile.am39
-rw-r--r--sys/dvb/cam.c33
-rw-r--r--sys/dvb/cam.h33
-rw-r--r--sys/dvb/camapplication.c292
-rw-r--r--sys/dvb/camapplication.h84
-rw-r--r--sys/dvb/camapplicationinfo.c153
-rw-r--r--sys/dvb/camapplicationinfo.h41
-rw-r--r--sys/dvb/camconditionalaccess.c208
-rw-r--r--sys/dvb/camconditionalaccess.h55
-rw-r--r--sys/dvb/camdevice.c213
-rw-r--r--sys/dvb/camdevice.h69
-rw-r--r--sys/dvb/camresourcemanager.c192
-rw-r--r--sys/dvb/camresourcemanager.h41
-rw-r--r--sys/dvb/camsession.c595
-rw-r--r--sys/dvb/camsession.h96
-rw-r--r--sys/dvb/camswclient.c162
-rw-r--r--sys/dvb/camswclient.h53
-rw-r--r--sys/dvb/camtransport.c505
-rw-r--r--sys/dvb/camtransport.h115
-rw-r--r--sys/dvb/camutils.c340
-rw-r--r--sys/dvb/camutils.h59
-rw-r--r--sys/dvb/dvbbasebin.c897
-rw-r--r--sys/dvb/dvbbasebin.h75
-rw-r--r--sys/dvb/gstdvb.c46
-rw-r--r--sys/dvb/gstdvbsrc.c156
-rw-r--r--sys/dvb/gstdvbsrc.h5
26 files changed, 4483 insertions, 74 deletions
diff --git a/sys/dvb/Makefile.am b/sys/dvb/Makefile.am
index 33d090b6..1bcb099f 100644
--- a/sys/dvb/Makefile.am
+++ b/sys/dvb/Makefile.am
@@ -1,9 +1,36 @@
-plugin_LTLIBRARIES = libgstdvbsrc.la
+plugin_LTLIBRARIES = libgstdvb.la
-libgstdvbsrc_la_SOURCES = gstdvbsrc.c
-libgstdvbsrc_la_CFLAGS = $(GST_CFLAGS)
-libgstdvbsrc_la_LIBADD = $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS)
-libgstdvbsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstdvb_la_SOURCES = \
+ gstdvb.c \
+ gstdvbsrc.c \
+ dvbbasebin.c \
+ cam.c \
+ camdevice.c \
+ camswclient.c \
+ camutils.c \
+ camtransport.c \
+ camsession.c \
+ camapplication.c \
+ camresourcemanager.c \
+ camapplicationinfo.c \
+ camconditionalaccess.c
-noinst_HEADERS = gstdvbsrc.h
+libgstdvb_la_CFLAGS = $(GST_CFLAGS)
+libgstdvb_la_LIBADD = $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS)
+libgstdvb_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = \
+ gstdvbsrc.h \
+ dvbbasebin.h \
+ cam.h \
+ camdevice.h \
+ camswclient.h \
+ camutils.h \
+ camtransport.h \
+ camsession.h \
+ camapplication.h \
+ camresourcemanager.h \
+ camapplicationinfo.h \
+ camconditionalaccess.h
+
diff --git a/sys/dvb/cam.c b/sys/dvb/cam.c
new file mode 100644
index 00000000..ec67888d
--- /dev/null
+++ b/sys/dvb/cam.c
@@ -0,0 +1,33 @@
+/*
+ * cam.c -
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "cam.h"
+
+#define GST_CAT_DEFAULT cam_debug_cat
+GST_DEBUG_CATEGORY (cam_debug_cat);
+
+void
+cam_init ()
+{
+ GST_DEBUG_CATEGORY_INIT (cam_debug_cat, "dvbcam", 0, "DVB CAM support");
+}
diff --git a/sys/dvb/cam.h b/sys/dvb/cam.h
new file mode 100644
index 00000000..3211a2d1
--- /dev/null
+++ b/sys/dvb/cam.h
@@ -0,0 +1,33 @@
+/*
+ * cam.h -
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CAM_H
+#define CAM_H
+
+#include <gst/gst.h>
+
+GST_DEBUG_CATEGORY_EXTERN (cam_debug_cat);
+
+void cam_init ();
+
+#endif /* CAM_H */
diff --git a/sys/dvb/camapplication.c b/sys/dvb/camapplication.c
new file mode 100644
index 00000000..03f9e552
--- /dev/null
+++ b/sys/dvb/camapplication.c
@@ -0,0 +1,292 @@
+/*
+ * camapplication.c - GStreamer CAM (EN50221) Application Layer
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "camapplication.h"
+
+#define GST_CAT_DEFAULT cam_debug_cat
+
+static CamReturn open_session_request_cb (CamSL * sl,
+ CamSLSession * session, CamSLResourceStatus * status);
+static CamReturn session_opened_cb (CamSL * sl, CamSLSession * session);
+static CamReturn session_closed_cb (CamSL * sl, CamSLSession * session);
+static CamReturn session_data_cb (CamSL * sl,
+ CamSLSession * session, guint8 * data, guint length);
+
+static guint
+resource_id_hash (gconstpointer key)
+{
+ guint resource_id = (guint) key;
+
+ if (!CAM_AL_RESOURCE_ID_IS_PUBLIC (resource_id)) {
+ /* private identifier, leave it as is */
+ return resource_id;
+ }
+
+ /* public identifier, mask out the version number */
+ return resource_id >> 6;
+}
+
+CamAL *
+cam_al_new (CamSL * sl)
+{
+ CamAL *al = g_new0 (CamAL, 1);
+
+ al->sl = sl;
+ al->applications = g_hash_table_new (resource_id_hash, g_direct_equal);
+
+ sl->user_data = al;
+ sl->open_session_request = open_session_request_cb;
+ sl->session_opened = session_opened_cb;
+ sl->session_closed = session_closed_cb;
+ sl->session_data = session_data_cb;
+
+ return al;
+}
+
+void
+cam_al_destroy (CamAL * al)
+{
+ g_hash_table_destroy (al->applications);
+ g_free (al);
+}
+
+gboolean
+cam_al_install (CamAL * al, CamALApplication * application)
+{
+ if (g_hash_table_lookup (al->applications,
+ GINT_TO_POINTER (application->resource_id)) != NULL)
+ return FALSE;
+
+ application->al = al;
+
+ g_hash_table_insert (al->applications,
+ GINT_TO_POINTER (application->resource_id), application);
+
+ return TRUE;
+}
+
+gboolean
+cam_al_uninstall (CamAL * al, CamALApplication * application)
+{
+ gboolean ret;
+
+ ret = g_hash_table_remove (al->applications,
+ GINT_TO_POINTER (application->resource_id));
+
+ return ret;
+}
+
+CamALApplication *
+cam_al_get (CamAL * al, guint resource_id)
+{
+ return CAM_AL_APPLICATION (g_hash_table_lookup (al->applications,
+ GINT_TO_POINTER (resource_id)));
+}
+
+void
+_cam_al_application_init (CamALApplication * application)
+{
+ application->sessions = NULL;
+}
+
+void
+_cam_al_application_destroy (CamALApplication * application)
+{
+ g_list_free (application->sessions);
+}
+
+static void
+foreach_get_key (gpointer key, gpointer value, gpointer user_data)
+{
+ GList **lst = (GList **) user_data;
+
+ *lst = g_list_append (*lst, key);
+}
+
+GList *
+cam_al_get_resource_ids (CamAL * al)
+{
+ GList *resource_ids = NULL;
+
+ g_hash_table_foreach (al->applications, foreach_get_key, &resource_ids);
+
+ return resource_ids;
+}
+
+void
+cam_al_calc_buffer_size (CamAL * al, guint body_length,
+ guint * buffer_size, guint * offset)
+{
+ guint apdu_header_length;
+ guint8 length_field_len;
+
+ /* get the length of the lenght_field() */
+ length_field_len = cam_calc_length_field_size (body_length);
+
+ /* sum the APDU header */
+ apdu_header_length = 3 + length_field_len;
+
+ /* chain up to the session layer to get the size of the buffer that can
+ * contain the whole APDU */
+ cam_sl_calc_buffer_size (al->sl, apdu_header_length + body_length,
+ buffer_size, offset);
+
+ /* add the APDU header to the SPDU offset */
+ *offset += apdu_header_length;
+}
+
+CamReturn
+cam_al_application_write (CamALApplication * application,
+ CamSLSession * session, guint tag, guint8 * buffer, guint buffer_size,
+ guint body_length)
+{
+ guint length_field_len;
+ guint apdu_header_length;
+ guint8 *apdu;
+
+ length_field_len = cam_calc_length_field_size (body_length);
+ apdu_header_length = 3 + length_field_len;
+ apdu = (buffer + buffer_size) - body_length - apdu_header_length;
+ apdu[0] = tag >> 16;
+ apdu[1] = (tag >> 8) & 0xFF;
+ apdu[2] = tag & 0xFF;
+
+ cam_write_length_field (&apdu[3], body_length);
+
+ return cam_sl_session_write (session, buffer, buffer_size,
+ apdu_header_length + body_length);
+}
+
+static CamReturn
+open_session_request_cb (CamSL * sl, CamSLSession * session,
+ CamSLResourceStatus * status)
+{
+ CamAL *al = CAM_AL (sl->user_data);
+ CamALApplication *application;
+ guint resource_id = session->resource_id;
+ CamReturn ret;
+
+ application = g_hash_table_lookup (al->applications,
+ GINT_TO_POINTER (resource_id));
+ if (application == NULL) {
+ *status = CAM_SL_RESOURCE_STATUS_NOT_FOUND;
+
+ return CAM_RETURN_OK;
+ }
+
+ if (CAM_AL_RESOURCE_ID_VERSION (application->resource_id)
+ < CAM_AL_RESOURCE_ID_VERSION (resource_id)) {
+ *status = CAM_SL_RESOURCE_STATUS_INVALID_VERSION;
+
+ return CAM_RETURN_OK;
+ }
+
+ ret = application->session_request (application, session, status);
+ if (CAM_FAILED (ret)) {
+ *status = CAM_SL_RESOURCE_STATUS_NOT_FOUND;
+
+ return ret;
+ }
+
+ if (*status == CAM_SL_RESOURCE_STATUS_OPEN) {
+ session->user_data = application;
+ application->sessions = g_list_append (application->sessions, session);
+ }
+
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+session_opened_cb (CamSL * sl, CamSLSession * session)
+{
+ CamALApplication *application;
+
+ application = CAM_AL_APPLICATION (session->user_data);
+ if (application == NULL) {
+ GST_ERROR ("session is established but has no application");
+ return CAM_RETURN_APPLICATION_ERROR;
+ }
+
+ return application->open (application, session);
+}
+
+static CamReturn
+session_closed_cb (CamSL * sl, CamSLSession * session)
+{
+ CamALApplication *application;
+ CamReturn ret;
+ GList *walk;
+
+ application = CAM_AL_APPLICATION (session->user_data);
+ if (application == NULL) {
+ GST_ERROR ("session is established but has no application");
+ return CAM_RETURN_APPLICATION_ERROR;
+ }
+
+ ret = application->close (application, session);
+ for (walk = application->sessions; walk; walk = g_list_next (walk)) {
+ CamSLSession *s = CAM_SL_SESSION (walk->data);
+
+ if (s->session_nb == session->session_nb) {
+ application->sessions = g_list_delete_link (application->sessions, walk);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static CamReturn
+session_data_cb (CamSL * sl, CamSLSession * session, guint8 * data, guint size)
+{
+ CamALApplication *application;
+ guint tag = 0;
+ guint8 length_field_len;
+ guint length;
+ guint i;
+
+ application = CAM_AL_APPLICATION (session->user_data);
+ if (application == NULL) {
+ GST_ERROR ("session is established but has no application");
+ return CAM_RETURN_APPLICATION_ERROR;
+ }
+
+ if (size < 4) {
+ GST_ERROR ("invalid APDU length %d", size);
+ return CAM_RETURN_APPLICATION_ERROR;
+ }
+
+ for (i = 0; i < 3; ++i)
+ tag = (tag << 8) | data[i];
+
+ length_field_len = cam_read_length_field (&data[3], &length);
+
+ if (length != size - 4) {
+ GST_ERROR ("unexpected APDU length %d expected %d", length, size);
+
+ return CAM_RETURN_APPLICATION_ERROR;
+ }
+
+ return application->data (application, session,
+ tag, data + 3 + length_field_len, length);
+}
diff --git a/sys/dvb/camapplication.h b/sys/dvb/camapplication.h
new file mode 100644
index 00000000..326d1f9b
--- /dev/null
+++ b/sys/dvb/camapplication.h
@@ -0,0 +1,84 @@
+/*
+ * camapplication.h - GStreamer CAM (EN50221) Application Layer
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CAM_APPLICATION_LAYER_H
+#define CAM_APPLICATION_LAYER_H
+
+#include "cam.h"
+#include "camsession.h"
+
+#define CAM_AL(obj) ((CamAL *) obj)
+#define CAM_AL_APPLICATION(obj) ((CamALApplication *) obj)
+
+#define CAM_AL_RESOURCE_ID_IS_PUBLIC(resource_id) ((resource_id >> 30) != 3)
+#define CAM_AL_RESOURCE_ID_CLASS(resource_id) ((resource_id >> 16) & 0x3FFF)
+#define CAM_AL_RESOURCE_ID_TYPE(resource_id) ((resource_id >> 6) & 0x03FF)
+#define CAM_AL_RESOURCE_ID_VERSION(resource_id) (resource_id & 0x3F)
+
+#define CAM_AL_RESOURCE_MANAGER_ID 0x10041
+#define CAM_AL_APPLICATION_INFO_ID 0x20041
+#define CAM_AL_CONDITIONAL_ACCESS_ID 0x30041
+
+typedef struct _CamAL CamAL;
+typedef struct _CamALApplication CamALApplication;
+
+struct _CamAL
+{
+ CamSL *sl;
+
+ GHashTable *applications;
+};
+
+struct _CamALApplication
+{
+ CamAL *al;
+ guint resource_id;
+ GList *sessions;
+
+ /* vtable */
+ CamReturn (*session_request) (CamALApplication *application,
+ CamSLSession *session, CamSLResourceStatus *status);
+ CamReturn (*open) (CamALApplication *application, CamSLSession *session);
+ CamReturn (*close) (CamALApplication *application, CamSLSession *session);
+ CamReturn (*data) (CamALApplication *application, CamSLSession *session,
+ guint tag, guint8 *buffer, guint length);
+};
+
+CamAL *cam_al_new (CamSL *sl);
+void cam_al_destroy (CamAL *al);
+
+gboolean cam_al_install (CamAL *al, CamALApplication *application);
+gboolean cam_al_uninstall (CamAL *al, CamALApplication *application);
+CamALApplication *cam_al_get (CamAL *al, guint resource_id);
+GList *cam_al_get_resource_ids (CamAL *al);
+
+void cam_al_calc_buffer_size (CamAL *al, guint body_length,
+ guint *buffer_size, guint *offset);
+
+void _cam_al_application_init (CamALApplication *application);
+void _cam_al_application_destroy (CamALApplication *application);
+
+CamReturn cam_al_application_write (CamALApplication *application,
+ CamSLSession *session, guint tag, guint8 *buffer,
+ guint buffer_size, guint body_length);
+#endif /* CAM_APPLICATION_LAYER_H */
diff --git a/sys/dvb/camapplicationinfo.c b/sys/dvb/camapplicationinfo.c
new file mode 100644
index 00000000..63a885a1
--- /dev/null
+++ b/sys/dvb/camapplicationinfo.c
@@ -0,0 +1,153 @@
+/*
+ * camapplicationinfo.h - CAM (EN50221) Application Info resource
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "camapplicationinfo.h"
+
+#define GST_CAT_DEFAULT cam_debug_cat
+#define TAG_APPLICATION_INFO_ENQUIRY 0x9F8020
+#define TAG_APPLICATION_INFO_REPLY 0x9F8021
+#define TAG_APPLICATION_INFO_ENTER_MENU 0x9F8022
+
+static CamReturn session_request_impl (CamALApplication * application,
+ CamSLSession * session, CamSLResourceStatus * status);
+static CamReturn open_impl (CamALApplication * application,
+ CamSLSession * session);
+static CamReturn close_impl (CamALApplication * application,
+ CamSLSession * session);
+static CamReturn data_impl (CamALApplication * application,
+ CamSLSession * session, guint tag, guint8 * buffer, guint length);
+
+CamApplicationInfo *
+cam_application_info_new ()
+{
+ CamApplicationInfo *info;
+ CamALApplication *application;
+
+ info = g_new0 (CamApplicationInfo, 1);
+ application = CAM_AL_APPLICATION (info);
+ _cam_al_application_init (application);
+ application->resource_id = CAM_AL_APPLICATION_INFO_ID;
+ application->session_request = session_request_impl;
+ application->open = open_impl;
+ application->close = close_impl;
+ application->data = data_impl;
+
+ return info;
+}
+
+void
+cam_application_info_destroy (CamApplicationInfo * info)
+{
+ _cam_al_application_destroy (CAM_AL_APPLICATION (info));
+ g_free (info);
+}
+
+static CamReturn
+send_simple (CamApplicationInfo * info, CamSLSession * session, guint tag)
+{
+ guint8 *buffer;
+ guint offset;
+ guint buffer_size;
+ CamReturn ret;
+
+ cam_al_calc_buffer_size (CAM_AL_APPLICATION (info)->al, 0, &buffer_size,
+ &offset);
+ buffer = g_malloc (buffer_size);
+
+ ret = cam_al_application_write (CAM_AL_APPLICATION (info), session,
+ tag, buffer, buffer_size, 0);
+
+ g_free (buffer);
+
+ return ret;
+}
+
+static CamReturn
+send_application_info_enquiry (CamApplicationInfo * info,
+ CamSLSession * session)
+{
+ GST_DEBUG ("sending application info enquiry");
+ return send_simple (info, session, TAG_APPLICATION_INFO_ENQUIRY);
+}
+
+static CamReturn
+session_request_impl (CamALApplication * application,
+ CamSLSession * session, CamSLResourceStatus * status)
+{
+ *status = CAM_SL_RESOURCE_STATUS_OPEN;
+
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+open_impl (CamALApplication * application, CamSLSession * session)
+{
+ CamApplicationInfo *info = CAM_APPLICATION_INFO (application);
+
+ return send_application_info_enquiry (info, session);
+}
+
+static CamReturn
+close_impl (CamALApplication * application, CamSLSession * session)
+{
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+handle_application_info_reply (CamApplicationInfo * info,
+ CamSLSession * session, guint8 * buffer, guint length)
+{
+ guint8 type;
+ guint8 menu_length;
+ gchar menu[255];
+
+ type = buffer[0];
+ menu_length = buffer[5];
+ menu_length = MIN (menu_length, 255);
+ memcpy (menu, buffer + 6, menu_length);
+ menu[menu_length] = 0;
+
+ GST_INFO ("application info reply, type: %d, menu: %s", type, menu);
+
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+data_impl (CamALApplication * application, CamSLSession * session,
+ guint tag, guint8 * buffer, guint length)
+{
+ CamReturn ret;
+ CamApplicationInfo *info = CAM_APPLICATION_INFO (application);
+
+ switch (tag) {
+ case TAG_APPLICATION_INFO_REPLY:
+ ret = handle_application_info_reply (info, session, buffer, length);
+ break;
+ default:
+ g_return_val_if_reached (CAM_RETURN_ERROR);
+ }
+
+ return ret;
+}
diff --git a/sys/dvb/camapplicationinfo.h b/sys/dvb/camapplicationinfo.h
new file mode 100644
index 00000000..c8aa8fab
--- /dev/null
+++ b/sys/dvb/camapplicationinfo.h
@@ -0,0 +1,41 @@
+/*
+ * camapplicationinfo.h - CAM (EN50221) Application Info resource
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CAM_APPLICATION_INFO_H
+#define CAM_APPLICATION_INFO_H
+
+#include "camapplication.h"
+
+#define CAM_APPLICATION_INFO(obj) ((CamApplicationInfo *) obj)
+
+typedef struct _CamApplicationInfo CamApplicationInfo;
+
+struct _CamApplicationInfo
+{
+ CamALApplication application;
+};
+
+CamApplicationInfo *cam_application_info_new ();
+void cam_application_info_destroy (CamApplicationInfo *info);
+
+#endif /* CAM_APPLICATION_INFO_H */
diff --git a/sys/dvb/camconditionalaccess.c b/sys/dvb/camconditionalaccess.c
new file mode 100644
index 00000000..985ce293
--- /dev/null
+++ b/sys/dvb/camconditionalaccess.c
@@ -0,0 +1,208 @@
+/*
+ * camconditionalaccess.c - CAM (EN50221) Conditional Access resource
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include "camutils.h"
+#include "camconditionalaccess.h"
+
+#define GST_CAT_DEFAULT cam_debug_cat
+#define TAG_CONDITIONAL_ACCESS_INFO_ENQUIRY 0x9F8030
+#define TAG_CONDITIONAL_ACCESS_INFO_REPLY 0x9F8031
+#define TAG_CONDITIONAL_ACCESS_PMT 0x9F8032
+
+static CamReturn session_request_impl (CamALApplication * application,
+ CamSLSession * session, CamSLResourceStatus * status);
+static CamReturn open_impl (CamALApplication * application,
+ CamSLSession * session);
+static CamReturn close_impl (CamALApplication * application,
+ CamSLSession * session);
+static CamReturn data_impl (CamALApplication * application,
+ CamSLSession * session, guint tag, guint8 * buffer, guint length);
+
+CamConditionalAccess *
+cam_conditional_access_new ()
+{
+ CamConditionalAccess *cas;
+ CamALApplication *application;
+
+ cas = g_new0 (CamConditionalAccess, 1);
+
+ application = CAM_AL_APPLICATION (cas);
+ _cam_al_application_init (application);
+ application->resource_id = CAM_AL_CONDITIONAL_ACCESS_ID;
+ application->session_request = session_request_impl;
+ application->open = open_impl;
+ application->close = close_impl;
+ application->data = data_impl;
+
+ cas->ready = FALSE;
+
+ return cas;
+}
+
+void
+cam_conditional_access_destroy (CamConditionalAccess * cas)
+{
+ _cam_al_application_destroy (CAM_AL_APPLICATION (cas));
+ g_free (cas);
+}
+
+static CamReturn
+send_ca_pmt (CamConditionalAccess * cas, GObject * pmt,
+ guint8 list_management, guint8 cmd_id)
+{
+ CamReturn ret;
+ guint8 *buffer;
+ guint buffer_size;
+ guint offset;
+ guint8 *ca_pmt;
+ guint ca_pmt_size;
+ GList *walk;
+
+ ca_pmt = cam_build_ca_pmt (pmt, list_management, cmd_id, &ca_pmt_size);
+ cam_al_calc_buffer_size (CAM_AL_APPLICATION (cas)->al,
+ ca_pmt_size, &buffer_size, &offset);
+
+ buffer = g_malloc0 (buffer_size);
+ memcpy (buffer + offset, ca_pmt, ca_pmt_size);
+
+ for (walk = CAM_AL_APPLICATION (cas)->sessions; walk; walk = walk->next) {
+ CamSLSession *session = CAM_SL_SESSION (walk->data);
+
+ ret = cam_al_application_write (CAM_AL_APPLICATION (cas), session,
+ TAG_CONDITIONAL_ACCESS_PMT, buffer, buffer_size, ca_pmt_size);
+ if (CAM_FAILED (ret)) {
+ GST_ERROR ("error sending ca_pmt to slot %d, error: %d",
+ session->connection->slot, ret);
+ continue;
+ }
+ }
+
+ g_free (ca_pmt);
+ g_free (buffer);
+
+ return CAM_RETURN_OK;
+}
+
+CamReturn
+cam_conditional_access_set_pmt (CamConditionalAccess * cas,
+ GObject * pmt, CamConditionalAccessPmtFlag flag)
+{
+ return send_ca_pmt (cas, pmt, flag, 0x01 /* ok_descrambling */ );
+}
+
+static CamReturn
+send_simple (CamConditionalAccess * cas, CamSLSession * session, guint tag)
+{
+ guint8 *buffer;
+ guint offset;
+ guint buffer_size;
+ CamReturn ret;
+
+ cam_al_calc_buffer_size (CAM_AL_APPLICATION (cas)->al, 0, &buffer_size,
+ &offset);
+ buffer = g_malloc (buffer_size);
+
+ ret = cam_al_application_write (CAM_AL_APPLICATION (cas), session,
+ tag, buffer, buffer_size, 0);
+
+ g_free (buffer);
+
+ return ret;
+}
+
+static CamReturn
+send_conditional_access_enquiry (CamConditionalAccess * cas,
+ CamSLSession * session)
+{
+ GST_DEBUG ("sending application cas enquiry");
+ return send_simple (cas, session, TAG_CONDITIONAL_ACCESS_INFO_ENQUIRY);
+}
+
+static CamReturn
+session_request_impl (CamALApplication * application,
+ CamSLSession * session, CamSLResourceStatus * status)
+{
+ *status = CAM_SL_RESOURCE_STATUS_OPEN;
+
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+open_impl (CamALApplication * application, CamSLSession * session)
+{
+ CamConditionalAccess *cas = CAM_CONDITIONAL_ACCESS (application);
+
+ GST_INFO ("opening conditional access session %d", session->session_nb);
+
+ return send_conditional_access_enquiry (cas, session);
+}
+
+static CamReturn
+close_impl (CamALApplication * application, CamSLSession * session)
+{
+ GST_INFO ("closing conditional access session %d", session->session_nb);
+
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+handle_conditional_access_info_reply (CamConditionalAccess * cas,
+ CamSLSession * session, guint8 * buffer, guint length)
+{
+ int i;
+ guint16 cas_id;
+
+ GST_INFO ("conditional access info enquiry reply");
+
+ for (i = 0; i < length / 2; ++i) {
+ cas_id = GST_READ_UINT16_BE (buffer);
+
+ GST_INFO ("slot %d, cas_id 0x%x", session->connection->slot, cas_id);
+
+ buffer += 2;
+ }
+
+ cas->ready = TRUE;
+
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+data_impl (CamALApplication * application, CamSLSession * session,
+ guint tag, guint8 * buffer, guint length)
+{
+ CamReturn ret;
+ CamConditionalAccess *cas = CAM_CONDITIONAL_ACCESS (application);
+
+ switch (tag) {
+ case TAG_CONDITIONAL_ACCESS_INFO_REPLY:
+ ret = handle_conditional_access_info_reply (cas, session, buffer, length);
+ break;
+ default:
+ g_return_val_if_reached (CAM_RETURN_ERROR);
+ }
+
+ return ret;
+}
diff --git a/sys/dvb/camconditionalaccess.h b/sys/dvb/camconditionalaccess.h
new file mode 100644
index 00000000..82c54d17
--- /dev/null
+++ b/sys/dvb/camconditionalaccess.h
@@ -0,0 +1,55 @@
+/*
+ * camconditionalaccess.h - CAM (EN50221) Conditional Access resource
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef CAM_CONDITIONAL_ACCESS_H
+#define CAM_CONDITIONAL_ACCESS_H
+
+#include "camapplication.h"
+
+#define CAM_CONDITIONAL_ACCESS(obj) ((CamConditionalAccess *) obj)
+
+typedef enum
+{
+ CAM_CONDITIONAL_ACCESS_PMT_FLAG_MORE = 0,
+ CAM_CONDITIONAL_ACCESS_PMT_FLAG_FIRST,
+ CAM_CONDITIONAL_ACCESS_PMT_FLAG_LAST,
+ CAM_CONDITIONAL_ACCESS_PMT_FLAG_ONLY,
+ CAM_CONDITIONAL_ACCESS_PMT_FLAG_ADD,
+ CAM_CONDITIONAL_ACCESS_PMT_FLAG_UPDATE,
+} CamConditionalAccessPmtFlag;
+
+typedef struct _CamConditionalAccess CamConditionalAccess;
+
+struct _CamConditionalAccess
+{
+ CamALApplication application;
+ gboolean ready;
+};
+
+CamConditionalAccess *cam_conditional_access_new ();
+void cam_conditional_access_destroy (CamConditionalAccess *cas);
+
+CamReturn cam_conditional_access_set_pmt (CamConditionalAccess *cas,
+ GObject *pmt, CamConditionalAccessPmtFlag flag);
+
+#endif /* CAM_CONDITIONAL_ACCESS_H */
diff --git a/sys/dvb/camdevice.c b/sys/dvb/camdevice.c
new file mode 100644
index 00000000..174edca4
--- /dev/null
+++ b/sys/dvb/camdevice.c
@@ -0,0 +1,213 @@
+/*
+ * camdevice.c - GStreamer hardware CAM support
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <linux/dvb/ca.h>
+
+#include "camdevice.h"
+
+#define GST_CAT_DEFAULT cam_debug_cat
+
+CamDevice *
+cam_device_new ()
+{
+ CamDevice *device = g_new0 (CamDevice, 1);
+
+ device->state = CAM_DEVICE_STATE_CLOSED;
+
+ return device;
+}
+
+static void
+reset_state (CamDevice * device)
+{
+ if (device->filename) {
+ g_free (device->filename);
+ device->filename = NULL;
+ }
+
+ if (device->fd) {
+ close (device->fd);
+ device->fd = -1;
+ }
+
+ if (device->cas) {
+ cam_conditional_access_destroy (device->cas);
+ device->cas = NULL;
+ }
+
+ if (device->mgr) {
+ cam_resource_manager_destroy (device->mgr);
+ device->mgr = NULL;
+ }
+
+ if (device->info) {
+ cam_application_info_destroy (device->info);
+ device->info = NULL;
+ }
+
+ if (device->al) {
+ cam_al_destroy (device->al);
+ device->al = NULL;
+ }
+
+ if (device->sl) {
+ cam_sl_destroy (device->sl);
+ device->sl = NULL;
+ }
+
+ if (device->tl) {
+ cam_tl_destroy (device->tl);
+ device->tl = NULL;
+ }
+
+ device->state = CAM_DEVICE_STATE_CLOSED;
+}
+
+void
+cam_device_free (CamDevice * device)
+{
+ if (device->state != CAM_DEVICE_STATE_CLOSED)
+ GST_WARNING ("device not in CLOSED state when free'd");
+
+ reset_state (device);
+ g_free (device);
+}
+
+gboolean
+cam_device_open (CamDevice * device, const char *filename)
+{
+ ca_caps_t ca_caps;
+ int ret;
+ int i;
+
+ g_return_val_if_fail (device != NULL, FALSE);
+ g_return_val_if_fail (device->state == CAM_DEVICE_STATE_CLOSED, FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ GST_INFO ("opening ca device %s", filename);
+
+ ret = open (filename, O_RDWR);
+ if (ret == -1) {
+ GST_ERROR ("can't open ca device: %s", strerror (errno));
+ return FALSE;
+ }
+
+ device->fd = ret;
+
+ ret = ioctl (device->fd, CA_RESET);
+ sleep (1);
+
+ /* get the capabilities of the CA */
+ ret = ioctl (device->fd, CA_GET_CAP, &ca_caps);
+ if (ret == -1) {
+ GST_ERROR ("CA_GET_CAP ioctl failed: %s", strerror (errno));
+ reset_state (device);
+ return FALSE;
+ }
+
+ device->tl = cam_tl_new (device->fd);
+ device->sl = cam_sl_new (device->tl);
+ device->al = cam_al_new (device->sl);
+
+ device->mgr = cam_resource_manager_new ();
+ cam_al_install (device->al, CAM_AL_APPLICATION (device->mgr));
+
+ device->info = cam_application_info_new ();
+ cam_al_install (device->al, CAM_AL_APPLICATION (device->info));
+
+ device->cas = cam_conditional_access_new ();
+ cam_al_install (device->al, CAM_AL_APPLICATION (device->cas));
+
+ /* open a connection to each slot */
+ for (i = 0; i < ca_caps.slot_num; ++i) {
+ CamTLConnection *connection;
+
+ ret = cam_tl_create_connection (device->tl, i, &connection);
+ if (CAM_FAILED (ret)) {
+ /* just ignore the slot, error out later only if no connection has been
+ * established */
+ GST_WARNING ("connection to slot %d failed, error: %d", i, ret);
+ continue;
+ }
+ }
+
+ if (g_hash_table_size (device->tl->connections) == 0) {
+ GST_ERROR ("couldn't connect to any slot");
+
+ reset_state (device);
+ return FALSE;
+ }
+
+ device->state = CAM_DEVICE_STATE_OPEN;
+ device->filename = g_strdup (filename);
+
+ /* poll each connection to initiate the protocol */
+ cam_tl_read_all (device->tl, TRUE);
+
+ return TRUE;
+}
+
+void
+cam_device_close (CamDevice * device)
+{
+ g_return_if_fail (device != NULL);
+ g_return_if_fail (device->state == CAM_DEVICE_STATE_OPEN);
+
+ GST_INFO ("closing ca device %s", device->filename);
+ reset_state (device);
+}
+
+void
+cam_device_poll (CamDevice * device)
+{
+ g_return_if_fail (device != NULL);
+ g_return_if_fail (device->state == CAM_DEVICE_STATE_OPEN);
+
+ cam_tl_read_all (device->tl, TRUE);
+}
+
+gboolean
+cam_device_ready (CamDevice * device)
+{
+ g_return_val_if_fail (device != NULL, FALSE);
+ g_return_val_if_fail (device->state == CAM_DEVICE_STATE_OPEN, FALSE);
+
+ return device->cas->ready;
+}
+
+void
+cam_device_set_pmt (CamDevice * device,
+ GObject * pmt, CamConditionalAccessPmtFlag flag)
+{
+ g_return_if_fail (device != NULL);
+ g_return_if_fail (device->state == CAM_DEVICE_STATE_OPEN);
+ g_return_if_fail (pmt != NULL);
+
+ cam_conditional_access_set_pmt (device->cas, pmt, flag);
+ cam_tl_read_all (device->tl, FALSE);
+}
diff --git a/sys/dvb/camdevice.h b/sys/dvb/camdevice.h
new file mode 100644
index 00000000..2dbc79df
--- /dev/null
+++ b/sys/dvb/camdevice.h
@@ -0,0 +1,69 @@
+/*
+ * camdevice.h - GStreamer hardware CAM interface
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CAM_DEVICE_H
+#define CAM_DEVICE_H
+
+#include "camtransport.h"
+#include "camsession.h"
+#include "camapplication.h"
+#include "camresourcemanager.h"
+#include "camapplicationinfo.h"
+#include "camconditionalaccess.h"
+
+typedef enum
+{
+ CAM_DEVICE_STATE_CLOSED,
+ CAM_DEVICE_STATE_OPEN,
+} CamDeviceState;
+
+typedef struct
+{
+ /* private */
+ CamDeviceState state;
+ char *filename;
+ int fd;
+
+ /* EN50221 layers */
+ CamTL *tl;
+ CamSL *sl;
+ CamAL *al;
+
+ /* apps provided by us */
+ CamResourceManager *mgr;
+ CamApplicationInfo *info;
+ CamConditionalAccess *cas;
+} CamDevice;
+
+CamDevice *cam_device_new ();
+void cam_device_free (CamDevice *device);
+
+gboolean cam_device_open (CamDevice *device, const char *filename);
+void cam_device_close (CamDevice *device);
+
+gboolean cam_device_ready (CamDevice *device);
+void cam_device_poll (CamDevice *device);
+void cam_device_set_pmt (CamDevice *device,
+ GObject *pmt, CamConditionalAccessPmtFlag flag);
+
+#endif /* CAM_DEVICE_H */
diff --git a/sys/dvb/camresourcemanager.c b/sys/dvb/camresourcemanager.c
new file mode 100644
index 00000000..8b7ef3b4
--- /dev/null
+++ b/sys/dvb/camresourcemanager.c
@@ -0,0 +1,192 @@
+/*
+ * camresourcemanager.c - GStreamer CAM (EN50221) Resource Manager
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "camresourcemanager.h"
+
+#define GST_CAT_DEFAULT cam_debug_cat
+#define TAG_PROFILE_ENQUIRY 0x9F8010
+#define TAG_PROFILE_REPLY 0x9F8011
+#define TAG_PROFILE_CHANGE 0x9F8012
+
+static CamReturn session_request_impl (CamALApplication * application,
+ CamSLSession * session, CamSLResourceStatus * status);
+static CamReturn open_impl (CamALApplication * application,
+ CamSLSession * session);
+static CamReturn close_impl (CamALApplication * application,
+ CamSLSession * session);
+static CamReturn data_impl (CamALApplication * application,
+ CamSLSession * session, guint tag, guint8 * buffer, guint length);
+
+CamResourceManager *
+cam_resource_manager_new ()
+{
+ CamALApplication *application;
+ CamResourceManager *mgr;
+
+ mgr = g_new0 (CamResourceManager, 1);
+ application = CAM_AL_APPLICATION (mgr);
+ _cam_al_application_init (application);
+ application->resource_id = CAM_AL_RESOURCE_MANAGER_ID;
+ application->session_request = session_request_impl;
+ application->open = open_impl;
+ application->close = close_impl;
+ application->data = data_impl;
+
+ return mgr;
+}
+
+void
+cam_resource_manager_destroy (CamResourceManager * mgr)
+{
+ _cam_al_application_destroy (CAM_AL_APPLICATION (mgr));
+ g_free (mgr);
+}
+
+static CamReturn
+session_request_impl (CamALApplication * application,
+ CamSLSession * session, CamSLResourceStatus * status)
+{
+ *status = CAM_SL_RESOURCE_STATUS_OPEN;
+
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+send_simple (CamResourceManager * mgr, CamSLSession * session, guint tag)
+{
+ guint8 *buffer;
+ guint offset;
+ guint buffer_size;
+ CamReturn ret;
+
+ cam_al_calc_buffer_size (CAM_AL_APPLICATION (mgr)->al, 0, &buffer_size,
+ &offset);
+ buffer = g_malloc (buffer_size);
+
+ ret = cam_al_application_write (CAM_AL_APPLICATION (mgr), session,
+ tag, buffer, buffer_size, 0);
+
+ g_free (buffer);
+
+ return ret;
+}
+
+static CamReturn
+send_profile_enquiry (CamResourceManager * mgr, CamSLSession * session)
+{
+ GST_DEBUG ("sending profile enquiry");
+ return send_simple (mgr, session, TAG_PROFILE_ENQUIRY);
+}
+
+static CamReturn
+send_profile_change (CamResourceManager * mgr, CamSLSession * session)
+{
+ GST_DEBUG ("sending profile change");
+ return send_simple (mgr, session, TAG_PROFILE_CHANGE);
+}
+
+static CamReturn
+send_profile_reply (CamResourceManager * mgr, CamSLSession * session)
+{
+ CamReturn ret;
+ guint8 *buffer;
+ guint8 *apdu_body;
+ guint buffer_size;
+ guint offset;
+ GList *resource_ids;
+ guint resource_ids_size;
+ GList *walk;
+
+ resource_ids = cam_al_get_resource_ids (CAM_AL_APPLICATION (mgr)->al);
+ resource_ids_size = g_list_length (resource_ids) * 4;
+
+ cam_al_calc_buffer_size (CAM_AL_APPLICATION (mgr)->al, resource_ids_size,
+ &buffer_size, &offset);
+
+ buffer = g_malloc (buffer_size);
+ apdu_body = buffer + offset;
+
+ for (walk = resource_ids; walk != NULL; walk = walk->next) {
+ GST_WRITE_UINT32_BE (apdu_body, walk->data);
+
+ apdu_body += 4;
+ }
+
+ g_list_free (resource_ids);
+
+ GST_DEBUG ("sending profile reply");
+ ret = cam_al_application_write (CAM_AL_APPLICATION (mgr), session,
+ TAG_PROFILE_REPLY, buffer, buffer_size, resource_ids_size);
+
+ g_free (buffer);
+
+ return ret;
+}
+
+static CamReturn
+open_impl (CamALApplication * application, CamSLSession * session)
+{
+ CamResourceManager *mgr = CAM_RESOURCE_MANAGER (application);
+
+ return send_profile_enquiry (mgr, session);
+}
+
+static CamReturn
+close_impl (CamALApplication * application, CamSLSession * session)
+{
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+handle_profile_reply (CamResourceManager * mgr, CamSLSession * session,
+ guint8 * buffer, guint length)
+{
+ /* the APDU contains length / 4 resource_ids */
+ /* FIXME: implement this once CamApplicationProxy is implemented */
+ GST_DEBUG ("got profile reply");
+ return send_profile_change (mgr, session);
+}
+
+static CamReturn
+data_impl (CamALApplication * application, CamSLSession * session,
+ guint tag, guint8 * buffer, guint length)
+{
+ CamReturn ret;
+ CamResourceManager *mgr = CAM_RESOURCE_MANAGER (application);
+
+ switch (tag) {
+ case TAG_PROFILE_ENQUIRY:
+ ret = send_profile_reply (mgr, session);
+ break;
+ case TAG_PROFILE_REPLY:
+ ret = handle_profile_reply (mgr, session, buffer, length);
+ break;
+ case TAG_PROFILE_CHANGE:
+ ret = send_profile_enquiry (mgr, session);
+ break;
+ default:
+ g_return_val_if_reached (CAM_RETURN_APPLICATION_ERROR);
+ }
+
+ return CAM_RETURN_OK;
+}
diff --git a/sys/dvb/camresourcemanager.h b/sys/dvb/camresourcemanager.h
new file mode 100644
index 00000000..b6fd0a47
--- /dev/null
+++ b/sys/dvb/camresourcemanager.h
@@ -0,0 +1,41 @@
+/*
+ * camresourcemanager.h - GStreamer CAM (EN50221) Resource Manager
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CAM_RESOURCE_MANAGER_H
+#define CAM_RESOURCE_MANAGER_H
+
+#include "camapplication.h"
+
+#define CAM_RESOURCE_MANAGER(obj) ((CamResourceManager *) obj)
+
+typedef struct _CamResourceManager CamResourceManager;
+
+struct _CamResourceManager
+{
+ CamALApplication application;
+};
+
+CamResourceManager *cam_resource_manager_new ();
+void cam_resource_manager_destroy (CamResourceManager *manager);
+
+#endif /* CAM_RESOURCE_MANAGER_H */
diff --git a/sys/dvb/camsession.c b/sys/dvb/camsession.c
new file mode 100644
index 00000000..6a2deb9c
--- /dev/null
+++ b/sys/dvb/camsession.c
@@ -0,0 +1,595 @@
+/*
+ * camsession.c - GStreamer CAM (EN50221) Session Layer
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "camsession.h"
+
+#define GST_CAT_DEFAULT cam_debug_cat
+#define I_TAG 0
+#define I_LENGTH_FB 1
+
+#define TAG_SESSION_NUMBER 0x90
+#define TAG_OPEN_SESSION_REQUEST 0x91
+#define TAG_OPEN_SESSION_RESPONSE 0x92
+#define TAG_CREATE_SESSION 0x93
+#define TAG_CREATE_SESSION_RESPONSE 0x94
+#define TAG_CLOSE_SESSION_REQUEST 0x95
+#define TAG_CLOSE_SESSION_RESPONSE 0x96
+
+static CamReturn connection_data_cb (CamTL * tl, CamTLConnection * connection,
+ guint8 * spdu, guint spdu_length);
+
+CamSLSession *
+cam_sl_session_new (CamSL * sl, CamTLConnection * connection,
+ guint16 session_nb, guint resource_id)
+{
+ CamSLSession *session = g_new0 (CamSLSession, 1);
+
+ session->state = CAM_SL_SESSION_STATE_IDLE;
+ session->sl = sl;
+ session->connection = connection;
+ session->session_nb = session_nb;
+ session->resource_id = resource_id;
+
+ return session;
+}
+
+void
+cam_sl_session_destroy (CamSLSession * session)
+{
+ g_free (session);
+}
+
+CamSL *
+cam_sl_new (CamTL * tl)
+{
+ CamSL *sl = g_new0 (CamSL, 1);
+
+ sl->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) cam_sl_session_destroy);
+
+ tl->user_data = sl;
+ tl->connection_data = connection_data_cb;
+
+ return sl;
+}
+
+void
+cam_sl_destroy (CamSL * sl)
+{
+ g_hash_table_destroy (sl->sessions);
+
+ g_free (sl);
+}
+
+CamReturn
+cam_sl_create_session (CamSL * sl,
+ CamTLConnection * connection, guint resource_id,
+ CamSLSession ** out_session)
+{
+ CamReturn ret;
+ CamSLSession *session = NULL;
+ guint size;
+ guint offset;
+ guint8 *tpdu = NULL;
+ guint8 *spdu;
+ guint16 session_nb;
+
+ /* FIXME: implement session number allocations properly */
+ if (sl->session_ids == G_MAXUINT16)
+ return CAM_RETURN_SESSION_TOO_MANY_SESSIONS;
+
+ session_nb = ++sl->session_ids;
+ session = cam_sl_session_new (sl, connection, session_nb, resource_id);
+
+ /* SPDU layout (8 bytes):
+ * TAG_CREATE_SESSION 1 byte
+ * length_field () 1 byte
+ * resource_id 4 bytes
+ * session_nb 2 bytes
+ */
+
+ /* get TPDU size */
+ cam_tl_calc_buffer_size (sl->tl, 8, &size, &offset);
+
+ tpdu = (guint8 *) g_malloc (size);
+ spdu = tpdu + offset;
+
+ /* SPDU header */
+ /* tag */
+ spdu[0] = TAG_CREATE_SESSION;
+ /* fixed length_field */
+ spdu[1] = 6;
+
+ /* SPDU body */
+ /* resource id */
+ GST_WRITE_UINT32_BE (&spdu[2], resource_id);
+ /* session_nb */
+ GST_WRITE_UINT16_BE (&spdu[6], session_nb);
+
+ /* write the TPDU */
+ ret = cam_tl_connection_write (session->connection, tpdu, size, 8);
+ if (CAM_FAILED (ret))
+ goto error;
+
+ *out_session = session;
+
+ g_free (tpdu);
+ return CAM_RETURN_OK;
+
+error:
+ if (session)
+ cam_sl_session_destroy (session);
+
+ g_free (tpdu);
+
+ return ret;
+}
+
+/* send a TAG_CLOSE_SESSION SPDU */
+CamReturn
+cam_sl_session_close (CamSLSession * session)
+{
+ CamReturn ret;
+ guint size;
+ guint offset;
+ guint8 *tpdu = NULL;
+ guint8 *spdu;
+ CamSL *sl = session->sl;
+
+ /* SPDU layout (4 bytes):
+ * TAG_CLOSE_SESSION 1 byte
+ * length_field () 1 byte
+ * session_nb 2 bytes
+ */
+
+ /* get the size of the TPDU */
+ cam_tl_calc_buffer_size (sl->tl, 4, &size, &offset);
+
+ tpdu = (guint8 *) g_malloc (size);
+ /* the spdu header starts after the TPDU headers */
+ spdu = tpdu + offset;
+
+ /* SPDU header */
+ /* tag */
+ spdu[0] = TAG_CLOSE_SESSION_REQUEST;
+ /* fixed length_field */
+ spdu[1] = 2;
+ /* SPDU body */
+ /* session_nb */
+ GST_WRITE_UINT16_BE (&spdu[2], session->session_nb);
+
+ /* write the TPDU */
+ ret = cam_tl_connection_write (session->connection, tpdu, size, 4);
+ if (CAM_FAILED (ret))
+ goto error;
+
+ session->state = CAM_SL_SESSION_STATE_CLOSING;
+
+ g_free (tpdu);
+
+ return CAM_RETURN_OK;
+
+error:
+ g_free (tpdu);
+
+ return ret;
+}
+
+void
+cam_sl_calc_buffer_size (CamSL * sl, guint body_length,
+ guint * buffer_size, guint * offset)
+{
+ /* an APDU is sent in a SESSION_NUMBER SPDU, which has a fixed header size (4
+ * bytes) */
+ cam_tl_calc_buffer_size (sl->tl, 4 + body_length, buffer_size, offset);
+ *offset += 4;
+}
+
+CamReturn
+cam_sl_session_write (CamSLSession * session,
+ guint8 * buffer, guint buffer_size, guint body_length)
+{
+ guint8 *spdu;
+
+ /* SPDU layout (4 + body_length bytes):
+ * TAG_SESSION_NUMBER (1 byte)
+ * length_field (1 byte)
+ * session number (2 bytes)
+ * one or more APDUs (body_length bytes)
+ */
+
+ spdu = (buffer + buffer_size) - body_length - 4;
+ spdu[0] = TAG_SESSION_NUMBER;
+ spdu[1] = 2;
+ GST_WRITE_UINT16_BE (&spdu[2], session->session_nb);
+
+ /* add our header to the body length */
+ return cam_tl_connection_write (session->connection,
+ buffer, buffer_size, 4 + body_length);
+}
+
+static CamReturn
+send_open_session_response (CamSL * sl, CamSLSession * session, guint8 status)
+{
+ CamReturn ret;
+ guint8 *tpdu;
+ guint size;
+ guint offset;
+ guint8 *spdu;
+
+ /* SPDU layout (9 bytes):
+ * TAG_OPEN_SESSION_RESPONSE 1 byte
+ * length_field () 1 byte
+ * session_status 1 byte
+ * resource_id 4 bytes
+ * session_nb 2 bytes
+ */
+
+ cam_tl_calc_buffer_size (session->sl->tl, 9, &size, &offset);
+
+ tpdu = g_malloc0 (size);
+ spdu = tpdu + offset;
+
+ spdu[0] = TAG_OPEN_SESSION_RESPONSE;
+ /* fixed length_field () */
+ spdu[1] = 7;
+ spdu[2] = status;
+ GST_WRITE_UINT32_BE (&spdu[3], session->resource_id);
+ GST_WRITE_UINT16_BE (&spdu[7], session->session_nb);
+
+ ret = cam_tl_connection_write (session->connection, tpdu, size, 9);
+ g_free (tpdu);
+ if (CAM_FAILED (ret))
+ return ret;
+
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+send_close_session_response (CamSL * sl, CamSLSession * session, guint8 status)
+{
+ CamReturn ret;
+ guint8 *tpdu;
+ guint size;
+ guint offset;
+ guint8 *spdu;
+
+ /* SPDU layout (5 bytes):
+ * TAG_CLOSE_SESSION_RESPONSE 1 byte
+ * length_field () 1 byte
+ * session_status 1 byte
+ * session_nb 2 bytes
+ */
+
+ cam_tl_calc_buffer_size (session->sl->tl, 5, &size, &offset);
+
+ tpdu = g_malloc0 (size);
+ spdu = tpdu + offset;
+
+ spdu[0] = TAG_OPEN_SESSION_RESPONSE;
+ /* fixed length_field() */
+ spdu[1] = 3;
+ spdu[2] = status;
+ GST_WRITE_UINT16_BE (&spdu[3], session->session_nb);
+
+ ret = cam_tl_connection_write (session->connection, tpdu, size, 5);
+ g_free (tpdu);
+ if (CAM_FAILED (ret))
+ return ret;
+
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+handle_open_session_request (CamSL * sl, CamTLConnection * connection,
+ guint8 * spdu, guint spdu_length)
+{
+ CamReturn ret;
+ guint resource_id;
+ guint status;
+ guint16 session_nb;
+ CamSLSession *session;
+
+ /* SPDU layout (6 bytes):
+ * TAG_OPEN_SESSION_REQUEST (1 byte)
+ * length_field() (1 byte)
+ * resource id (4 bytes)
+ */
+ if (spdu_length != 6) {
+ GST_ERROR ("expected OPEN_SESSION_REQUEST to be 6 bytes, got %d",
+ spdu_length);
+ return CAM_RETURN_SESSION_ERROR;
+ }
+
+ /* skip tag and length_field () */
+ resource_id = GST_READ_UINT32_BE (&spdu[2]);
+
+ /* create a new session */
+ if (sl->session_ids == G_MAXUINT16) {
+ GST_ERROR ("too many sessions opened");
+ return CAM_RETURN_SESSION_TOO_MANY_SESSIONS;
+ }
+
+ session_nb = ++sl->session_ids;
+ session = cam_sl_session_new (sl, connection, session_nb, resource_id);
+
+ GST_INFO ("session request: %d %x", session_nb, session->resource_id);
+
+ if (sl->open_session_request) {
+ /* forward the request to the upper layer */
+ ret = sl->open_session_request (sl, session, &status);
+ if (CAM_FAILED (ret))
+ goto error;
+ } else {
+ status = 0xF0;
+ }
+
+ ret = send_open_session_response (sl, session, (guint8) status);
+ if (CAM_FAILED (ret))
+ goto error;
+
+ GST_INFO ("session request response: %d %x", session_nb, status);
+
+ if (status == CAM_SL_RESOURCE_STATUS_OPEN) {
+ /* if the session has been accepted add it and signal */
+ session->state = CAM_SL_SESSION_STATE_ACTIVE;
+ g_hash_table_insert (sl->sessions,
+ GINT_TO_POINTER ((guint) session_nb), session);
+
+ if (sl->session_opened) {
+ /* notify the upper layer */
+ ret = sl->session_opened (sl, session);
+ if (CAM_FAILED (ret))
+ return ret;
+ }
+ } else {
+ /* session request wasn't accepted */
+ cam_sl_session_destroy (session);
+ }
+
+ return CAM_RETURN_OK;
+
+error:
+ cam_sl_session_destroy (session);
+
+ return ret;
+}
+
+static CamReturn
+handle_create_session_response (CamSL * sl, CamTLConnection * connection,
+ guint8 * spdu, guint spdu_length)
+{
+ guint8 status;
+ guint resource_id;
+ guint16 session_nb;
+ CamSLSession *session;
+
+ /* SPDU layout (9 bytes):
+ * TAG_CREATE_SESSION_RESPONSE (1 byte)
+ * length_field() (1 byte)
+ * status (1 byte)
+ * resource id (4 bytes)
+ * session number (2 bytes)
+ */
+ if (spdu_length != 9) {
+ GST_ERROR ("expected CREATE_SESSION_RESPONSE to be 9 bytes, got %d",
+ spdu_length);
+ return CAM_RETURN_SESSION_ERROR;
+ }
+
+ /* skip tag and length */
+ status = spdu[2];
+ resource_id = GST_READ_UINT32_BE (&spdu[3]);
+ session_nb = GST_READ_UINT16_BE (&spdu[7]);
+
+ session = g_hash_table_lookup (sl->sessions,
+ GINT_TO_POINTER ((guint) session_nb));
+ if (session == NULL) {
+ GST_DEBUG ("got CREATE_SESSION_RESPONSE for unknown session: %d",
+ session_nb);
+ return CAM_RETURN_SESSION_ERROR;
+ }
+
+ if (session->state == CAM_SL_SESSION_STATE_CLOSING) {
+ GST_DEBUG ("ignoring CREATE_SESSION_RESPONSE for closing session: %d",
+ session_nb);
+ return CAM_RETURN_OK;
+ }
+
+ session->state = CAM_SL_SESSION_STATE_ACTIVE;
+
+ GST_DEBUG ("session opened %d", session->session_nb);
+
+ if (sl->session_opened)
+ /* notify the upper layer */
+ return sl->session_opened (sl, session);
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+handle_close_session_request (CamSL * sl, CamTLConnection * connection,
+ guint8 * spdu, guint spdu_length)
+{
+ CamReturn ret;
+ guint16 session_nb;
+ CamSLSession *session;
+ guint8 status = 0;
+
+ /* SPDU layout (4 bytes):
+ * TAG_CLOSE_SESSION_REQUEST (1 byte)
+ * length_field () (1 byte)
+ * session number (2 bytes)
+ */
+ if (spdu_length != 4) {
+ GST_ERROR ("expected CLOSE_SESSION_REQUEST to be 4 bytes, got %d",
+ spdu_length);
+ return CAM_RETURN_SESSION_ERROR;
+ }
+
+ /* skip tag and length_field() */
+ session_nb = GST_READ_UINT16_BE (&spdu[2]);
+
+ GST_DEBUG ("close session request %d", session_nb);
+
+ session = g_hash_table_lookup (sl->sessions,
+ GINT_TO_POINTER ((guint) session_nb));
+ if (session == NULL) {
+ GST_WARNING ("got CLOSE_SESSION_REQUEST for unknown session: %d",
+ session_nb);
+
+ status = 0xF0;
+ } else if (session->state == CAM_SL_SESSION_STATE_CLOSING) {
+ GST_WARNING ("got CLOSE_SESSION_REQUEST for closing session: %d",
+ session_nb);
+
+ status = 0xF0;
+ }
+
+ GST_DEBUG ("close session response: %d %d", session->session_nb, status);
+
+ ret = send_close_session_response (sl, session, status);
+ if (CAM_FAILED (ret))
+ return ret;
+
+ if (session->state != CAM_SL_SESSION_STATE_CLOSING) {
+ GST_DEBUG ("session closed %d", session->session_nb);
+
+ if (sl->session_closed)
+ ret = sl->session_closed (sl, session);
+
+ g_hash_table_remove (sl->sessions,
+ GINT_TO_POINTER ((guint) session->session_nb));
+
+ if (CAM_FAILED (ret))
+ return ret;
+ }
+
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+handle_close_session_response (CamSL * sl, CamTLConnection * connection,
+ guint8 * spdu, guint spdu_length)
+{
+ guint16 session_nb;
+ CamSLSession *session;
+ CamReturn ret = CAM_RETURN_OK;
+
+ /* SPDU layout (5 bytes):
+ * TAG_CLOSE_SESSION_RESPONSE (1 byte)
+ * length_field () (1 byte)
+ * status (1 byte)
+ * session number (2 bytes)
+ */
+
+ if (spdu_length != 5) {
+ GST_ERROR ("expected CLOSE_SESSION_RESPONSE to be 5 bytes, got %d",
+ spdu_length);
+ return CAM_RETURN_SESSION_ERROR;
+ }
+
+ /* skip tag, length_field() and session_status */
+ session_nb = GST_READ_UINT16_BE (&spdu[3]);
+
+ session = g_hash_table_lookup (sl->sessions,
+ GINT_TO_POINTER ((guint) session_nb));
+ if (session == NULL || session->state != CAM_SL_SESSION_STATE_ACTIVE) {
+ GST_ERROR ("unexpected CLOSED_SESSION_RESPONSE");
+ return CAM_RETURN_SESSION_ERROR;
+ }
+
+ GST_DEBUG ("session closed %d", session->session_nb);
+
+ if (sl->session_closed)
+ ret = sl->session_closed (sl, session);
+
+ g_hash_table_remove (sl->sessions,
+ GINT_TO_POINTER ((guint) session->session_nb));
+
+ return ret;
+}
+
+static CamReturn
+handle_session_data (CamSL * sl, CamTLConnection * connection,
+ guint8 * spdu, guint length)
+{
+ guint16 session_nb;
+ CamSLSession *session;
+
+ /* SPDU layout (>= 4 bytes):
+ * TAG_SESSION_NUMBER (1 byte)
+ * length_field() (1 byte)
+ * session number (2 bytes)
+ * one or more APDUs
+ */
+
+ if (length < 4) {
+ GST_ERROR ("invalid SESSION_NUMBER SPDU length %d", length);
+ return CAM_RETURN_SESSION_ERROR;
+ }
+
+ session_nb = GST_READ_UINT16_BE (&spdu[2]);
+
+ session = g_hash_table_lookup (sl->sessions,
+ GINT_TO_POINTER ((guint) session_nb));
+ if (session == NULL) {
+ GST_ERROR ("got SESSION_NUMBER on an unknown connection: %d", session_nb);
+ return CAM_RETURN_SESSION_ERROR;
+ }
+
+ if (sl->session_data)
+ /* pass the APDUs to the upper layer, removing our 4-bytes header */
+ return sl->session_data (sl, session, spdu + 4, length - 4);
+
+ return CAM_RETURN_OK;
+}
+
+static CamReturn
+connection_data_cb (CamTL * tl, CamTLConnection * connection,
+ guint8 * spdu, guint spdu_length)
+{
+ CamReturn ret;
+ CamSL *sl = CAM_SL (tl->user_data);
+
+ switch (spdu[I_TAG]) {
+ case TAG_CREATE_SESSION_RESPONSE:
+ ret = handle_create_session_response (sl, connection, spdu, spdu_length);
+ break;
+ case TAG_OPEN_SESSION_REQUEST:
+ ret = handle_open_session_request (sl, connection, spdu, spdu_length);
+ break;
+ case TAG_CLOSE_SESSION_REQUEST:
+ ret = handle_close_session_request (sl, connection, spdu, spdu_length);
+ break;
+ case TAG_CLOSE_SESSION_RESPONSE:
+ ret = handle_close_session_response (sl, connection, spdu, spdu_length);
+ break;
+ case TAG_SESSION_NUMBER:
+ ret = handle_session_data (sl, connection, spdu, spdu_length);
+ break;
+ default:
+ g_return_val_if_reached (CAM_RETURN_SESSION_ERROR);
+ }
+
+ return ret;
+}
diff --git a/sys/dvb/camsession.h b/sys/dvb/camsession.h
new file mode 100644
index 00000000..7b9ac54b
--- /dev/null
+++ b/sys/dvb/camsession.h
@@ -0,0 +1,96 @@
+/*
+ * camsession.h - GStreamer CAM (EN50221) Session Layer
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CAM_SESSION_H
+#define CAM_SESSION_H
+
+#include "cam.h"
+#include "camtransport.h"
+
+#define CAM_SL(obj) ((CamSL *) obj)
+#define CAM_SL_SESSION(obj) ((CamSLSession *) obj)
+
+typedef struct _CamSL CamSL;
+typedef struct _CamSLSession CamSLSession;
+
+typedef enum
+{
+ CAM_SL_SESSION_STATE_IDLE,
+ CAM_SL_SESSION_STATE_OPENING,
+ CAM_SL_SESSION_STATE_ACTIVE,
+ CAM_SL_SESSION_STATE_CLOSING
+} CamSLSessionState;
+
+typedef enum
+{
+ CAM_SL_RESOURCE_STATUS_OPEN = 0x00,
+ CAM_SL_RESOURCE_STATUS_NOT_FOUND = 0xF0,
+ CAM_SL_RESOURCE_STATUS_UNAVAILABLE = 0xF1,
+ CAM_SL_RESOURCE_STATUS_INVALID_VERSION = 0xF2,
+ CAM_SL_RESOURCE_STATUS_BUSY = 0xF3
+} CamSLResourceStatus;
+
+struct _CamSL
+{
+ CamTL *tl;
+
+ GHashTable *sessions;
+ guint session_ids;
+
+ /* callbacks */
+ CamReturn (*open_session_request) (CamSL *sl, CamSLSession *session,
+ CamSLResourceStatus *status);
+ CamReturn (*session_opened) (CamSL *sl, CamSLSession *session);
+ CamReturn (*session_closed) (CamSL *sl, CamSLSession *session);
+ CamReturn (*session_data) (CamSL *sl, CamSLSession *session,
+ guint8 *data, guint length);
+
+ gpointer user_data;
+};
+
+struct _CamSLSession
+{
+ CamSL *sl;
+ CamTLConnection *connection;
+
+ guint resource_id;
+ guint16 session_nb;
+
+ CamSLSessionState state;
+
+ gpointer user_data;
+};
+
+CamSL *cam_sl_new (CamTL *transport);
+void cam_sl_destroy (CamSL *sl);
+
+CamReturn cam_sl_create_session (CamSL *sl, CamTLConnection *connection,
+ guint resource_id, CamSLSession **session);
+CamReturn cam_sl_session_close (CamSLSession *session);
+
+void cam_sl_calc_buffer_size (CamSL *sl,
+ guint body_length, guint *buffer_size, guint *offset);
+CamReturn cam_sl_session_write (CamSLSession *session,
+ guint8 *buffe, guint buffer_size, guint body_length);
+
+#endif /* CAM_SESSION_H */
diff --git a/sys/dvb/camswclient.c b/sys/dvb/camswclient.c
new file mode 100644
index 00000000..d956f3c9
--- /dev/null
+++ b/sys/dvb/camswclient.c
@@ -0,0 +1,162 @@
+/*
+ * camswclient.c - GStreamer softcam client
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <gst/gst.h>
+
+#include "camswclient.h"
+#include "cam.h"
+#include "camutils.h"
+
+#define GST_CAT_DEFAULT cam_debug_cat
+#define UNIX_PATH_MAX 108
+
+CamSwClient *
+cam_sw_client_new ()
+{
+ CamSwClient *client = g_new0 (CamSwClient, 1);
+
+ client->state = CAM_SW_CLIENT_STATE_CLOSED;
+
+ return client;
+}
+
+static void
+reset_state (CamSwClient * client)
+{
+ if (client->sock)
+ close (client->sock);
+
+ if (client->sock_path)
+ g_free (client->sock_path);
+}
+
+void
+cam_sw_client_free (CamSwClient * client)
+{
+ g_return_if_fail (client != NULL);
+
+ if (client->state != CAM_SW_CLIENT_STATE_CLOSED)
+ GST_WARNING ("client not in CLOSED state when free'd");
+
+ reset_state (client);
+ g_free (client);
+}
+
+gboolean
+cam_sw_client_open (CamSwClient * client, const char *sock_path)
+{
+ struct sockaddr_un addr;
+ int ret;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (client->state == CAM_SW_CLIENT_STATE_CLOSED, FALSE);
+ g_return_val_if_fail (sock_path != NULL, FALSE);
+
+ addr.sun_family = AF_UNIX;
+ strncpy (addr.sun_path, sock_path, UNIX_PATH_MAX);
+
+ GST_INFO ("connecting to softcam socket: %s", sock_path);
+ client->sock = socket (PF_UNIX, SOCK_STREAM, 0);
+ ret =
+ connect (client->sock, (struct sockaddr *) &addr,
+ sizeof (struct sockaddr_un));
+ if (ret != 0) {
+ GST_ERROR ("error opening softcam socket %s, error: %s",
+ sock_path, strerror (errno));
+
+ return FALSE;
+ }
+
+ client->sock_path = g_strdup (sock_path);
+ client->state = CAM_SW_CLIENT_STATE_OPEN;
+
+ return TRUE;
+}
+
+void
+cam_sw_client_close (CamSwClient * client)
+{
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (client->state == CAM_SW_CLIENT_STATE_OPEN);
+
+ reset_state (client);
+ client->state = CAM_SW_CLIENT_STATE_CLOSED;
+}
+
+static void
+send_ca_pmt (CamSwClient * client, GObject * pmt,
+ guint8 list_management, guint8 cmd_id)
+{
+ guint8 *buffer;
+ guint buffer_size;
+ guint8 *ca_pmt;
+ guint ca_pmt_size;
+ guint length_field_len;
+ guint header_len;
+
+ ca_pmt = cam_build_ca_pmt (pmt, list_management, cmd_id, &ca_pmt_size);
+
+ length_field_len = cam_calc_length_field_size (ca_pmt_size);
+ header_len = 3 + length_field_len;
+ buffer_size = header_len + ca_pmt_size;
+
+ buffer = g_malloc0 (buffer_size);
+ memcpy (buffer + header_len, ca_pmt, ca_pmt_size);
+
+ /* ca_pmt resource_id */
+ buffer[0] = 0x9F;
+ buffer[1] = 0x80;
+ buffer[2] = 0x32;
+
+ cam_write_length_field (&buffer[3], ca_pmt_size);
+
+ write (client->sock, buffer, buffer_size);
+
+ g_free (ca_pmt);
+ g_free (buffer);
+}
+
+void
+cam_sw_client_set_pmt (CamSwClient * client, GObject * pmt)
+{
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (pmt != NULL);
+
+ return send_ca_pmt (client, pmt, 0x03 /* only */ ,
+ 0x01 /* ok_descrambling */ );
+}
+
+void
+cam_sw_client_update_pmt (CamSwClient * client, GObject * pmt)
+{
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (pmt != NULL);
+
+ return send_ca_pmt (client, pmt, 0x05 /* update */ ,
+ 0x01 /* ok_descrambling */ );
+}
diff --git a/sys/dvb/camswclient.h b/sys/dvb/camswclient.h
new file mode 100644
index 00000000..5471e96a
--- /dev/null
+++ b/sys/dvb/camswclient.h
@@ -0,0 +1,53 @@
+/*
+ * camswclient.h - GStreamer softcam client
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CAM_SW_CLIENT_H
+#define CAM_SW_CLIENT_H
+
+#include <glib.h>
+
+typedef enum
+{
+ CAM_SW_CLIENT_STATE_CLOSED,
+ CAM_SW_CLIENT_STATE_OPEN,
+} CamSwClientState;
+
+typedef struct
+{
+ /* private */
+ CamSwClientState state;
+ char *sock_path;
+ int sock;
+
+} CamSwClient;
+
+CamSwClient *cam_sw_client_new ();
+void cam_sw_client_free (CamSwClient *sw_client);
+
+gboolean cam_sw_client_open (CamSwClient *sw_client, const char *sock_path);
+void cam_sw_client_close (CamSwClient *sw_client);
+
+void cam_sw_client_set_pmt (CamSwClient *sw_client, GObject *pmt);
+void cam_sw_client_update_pmt (CamSwClient *sw_client, GObject *pmt);
+
+#endif /* CAM_SW_CLIENT_H */
diff --git a/sys/dvb/camtransport.c b/sys/dvb/camtransport.c
new file mode 100644
index 00000000..288b2e46
--- /dev/null
+++ b/sys/dvb/camtransport.c
@@ -0,0 +1,505 @@
+/*
+ * camtransport.c - GStreamer CAM (EN50221) transport layer
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "camtransport.h"
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#define GST_CAT_DEFAULT cam_debug_cat
+#define READ_TIMEOUT_SEC 2
+#define READ_TIMEOUT_USEC 0
+
+#define POLL_INTERVAL 0.300
+
+#define TAG_SB 0x80
+#define TAG_RCV 0x81
+#define TAG_CREATE_T_C 0x82
+#define TAG_C_T_C_REPLY 0x83
+#define TAG_DELETE_T_C 0x84
+#define TAG_D_T_C_REPLY 0x85
+#define TAG_REQUEST_T_C 0x86
+#define TAG_NEW_T_C 0x87
+#define TAG_T_C_ERROR 0x88
+#define TAG_DATA_MORE 0xA1
+#define TAG_DATA_LAST 0xA0
+
+/* utility struct used to store the state of the connections in cam_tl_read_next
+ */
+typedef struct
+{
+ GList *active;
+ GList *idle;
+} CamTLConnectionsStatus;
+
+void cam_gst_util_dump_mem (const guchar * mem, guint size);
+
+CamTLConnection *
+cam_tl_connection_new (CamTL * tl, guint8 id)
+{
+ CamTLConnection *connection;
+
+ connection = g_new0 (CamTLConnection, 1);
+ connection->tl = tl;
+ connection->id = id;
+ connection->state = CAM_TL_CONNECTION_STATE_CLOSED;
+ connection->has_data = FALSE;
+
+ return connection;
+}
+
+static void
+cam_tl_connection_destroy (CamTLConnection * connection)
+{
+ if (connection->last_poll)
+ g_timer_destroy (connection->last_poll);
+
+ g_free (connection);
+}
+
+CamTL *
+cam_tl_new (int fd)
+{
+ CamTL *tl;
+
+ tl = g_new0 (CamTL, 1);
+ tl->fd = fd;
+ tl->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) cam_tl_connection_destroy);
+
+ return tl;
+}
+
+void
+cam_tl_destroy (CamTL * tl)
+{
+ g_hash_table_destroy (tl->connections);
+
+ g_free (tl);
+}
+
+/* read data from the module without blocking indefinitely */
+static CamReturn
+cam_tl_read_timeout (CamTL * tl, struct timeval *timeout)
+{
+ fd_set read_fd;
+ int sret;
+
+ FD_ZERO (&read_fd);
+ FD_SET (tl->fd, &read_fd);
+
+ sret = select (tl->fd + 1, &read_fd, NULL, NULL, timeout);
+ if (sret == 0) {
+ GST_DEBUG ("read timeout");
+ return CAM_RETURN_TRANSPORT_TIMEOUT;
+ }
+
+ tl->buffer_size = read (tl->fd, &tl->buffer, HOST_BUFFER_SIZE);
+ if (tl->buffer_size == -1) {
+ GST_ERROR ("error reading tpdu: %s", g_strerror (errno));
+ return CAM_RETURN_TRANSPORT_ERROR;
+ }
+
+ return CAM_RETURN_OK;
+}
+
+/* read data from the module using the default timeout */
+static CamReturn
+cam_tl_read (CamTL * tl)
+{
+ struct timeval timeout;
+
+ timeout.tv_sec = READ_TIMEOUT_SEC;
+ timeout.tv_usec = READ_TIMEOUT_USEC;
+
+ return cam_tl_read_timeout (tl, &timeout);
+}
+
+/* get the number of bytes to allocate for a TPDU with a body of body_length
+ * bytes. Also get the offset from the beginning of the buffer that marks the
+ * position of the first byte of the TPDU body */
+void
+cam_tl_calc_buffer_size (CamTL * tl, guint body_length,
+ guint * buffer_size, guint * offset)
+{
+ guint length_field_len;
+
+ /* the size of a TPDU is:
+ * 1 byte slot number
+ * 1 byte connection id
+ * length_field_len bytes length field
+ * 1 byte connection id
+ * body_length bytes body
+ */
+
+ /* get the length of the lenght_field block */
+ length_field_len = cam_calc_length_field_size (body_length);
+
+ *offset = 3 + length_field_len + 1;
+ *buffer_size = *offset + body_length;
+}
+
+/* write the header of a TPDU
+ * NOTE: this function assumes that the buffer is large enough to contain the
+ * complete TPDU (see cam_tl_calc_buffer_size ()) and that enough space has been
+ * left from the beginning of the buffer to write the TPDU header.
+ */
+static CamReturn
+cam_tl_connection_write_tpdu (CamTLConnection * connection,
+ guint8 tag, guint8 * buffer, guint buffer_size, guint body_length)
+{
+ int sret;
+ CamTL *tl = connection->tl;
+ guint8 length_field_len;
+
+ /* slot number */
+ buffer[0] = connection->slot;
+ /* connection number */
+ buffer[1] = connection->id;
+ /* tag */
+ buffer[2] = tag;
+ /* length can take 1 to 4 bytes */
+ length_field_len = cam_write_length_field (&buffer[3], body_length);
+ buffer[3 + length_field_len] = connection->id;
+
+ GST_DEBUG ("writing TPDU %x connection %d", buffer[2], connection->id);
+
+ //cam_gst_util_dump_mem (buffer, buffer_size);
+
+ sret = write (tl->fd, buffer, buffer_size);
+ if (sret == -1) {
+ GST_ERROR ("error witing TPDU (%d): %s", errno, g_strerror (errno));
+ return CAM_RETURN_TRANSPORT_ERROR;
+ }
+
+ tl->expected_tpdus += 1;
+
+ return CAM_RETURN_OK;
+}
+
+/* convenience function to write control TPDUs (TPDUs having a single-byte body)
+ */
+static CamReturn
+cam_tl_connection_write_control_tpdu (CamTLConnection * connection, guint8 tag)
+{
+ guint8 tpdu[5];
+
+ /* TPDU layout (5 bytes):
+ *
+ * slot number (1 byte)
+ * connection id (1 byte)
+ * tag (1 byte)
+ * length (1 byte)
+ * connection id (1 byte)
+ */
+
+ return cam_tl_connection_write_tpdu (connection, tag, tpdu, 5, 1);
+}
+
+/* read the next TPDU from the CAM */
+static CamReturn
+cam_tl_read_tpdu_next (CamTL * tl, CamTLConnection ** out_connection)
+{
+ CamReturn ret;
+ CamTLConnection *connection;
+ guint8 slot;
+ guint8 connection_id;
+ guint8 *tpdu;
+ guint8 length_field_len;
+ guint8 status;
+
+ ret = cam_tl_read (tl);
+ if (CAM_FAILED (ret))
+ return ret;
+
+ tpdu = tl->buffer;
+
+ /* must hold at least slot, connection_id, 1byte length_field, connection_id
+ */
+ if (tl->buffer_size < 4) {
+ GST_ERROR ("invalid TPDU length %d", tl->buffer_size);
+ return CAM_RETURN_TRANSPORT_ERROR;
+ }
+
+ /* LPDU slot */
+ slot = tpdu[0];
+ /* LPDU connection id */
+ connection_id = tpdu[1];
+
+ connection = g_hash_table_lookup (tl->connections,
+ GINT_TO_POINTER ((guint) connection_id));
+ if (connection == NULL) {
+ /* WHAT? */
+ GST_ERROR ("CAM sent a TPDU on an unknown connection: %d", connection_id);
+ return CAM_RETURN_TRANSPORT_ERROR;
+ }
+
+ /* read the length_field () */
+ length_field_len = cam_read_length_field (&tpdu[3], &tl->body_length);
+
+ if (tl->body_length + 3 > tl->buffer_size) {
+ GST_ERROR ("invalid TPDU length_field (%d) exceeds "
+ "the size of the buffer (%d)", tl->body_length, tl->buffer_size);
+ return CAM_RETURN_TRANSPORT_ERROR;
+ }
+
+ /* skip slot + connection id + tag + lenght_field () + connection id */
+ tl->body = tpdu + 4 + length_field_len;
+ /* do not count the connection id byte as part of the body */
+ tl->body_length -= 1;
+
+ if (tl->buffer[tl->buffer_size - 4] != TAG_SB) {
+ GST_ERROR ("no TAG_SB appended to TPDU");
+ return CAM_RETURN_TRANSPORT_ERROR;
+ }
+
+ status = tl->buffer[tl->buffer_size - 1];
+ if (status & 0x80) {
+ connection->has_data = TRUE;
+ } else {
+ connection->has_data = FALSE;
+ }
+
+ GST_DEBUG ("received TPDU %x more data %d", tpdu[2], connection->has_data);
+ tl->expected_tpdus -= 1;
+
+ *out_connection = connection;
+
+ return CAM_RETURN_OK;
+}
+
+/* create a connection with the module */
+CamReturn
+cam_tl_create_connection (CamTL * tl, guint8 slot,
+ CamTLConnection ** connection)
+{
+ CamReturn ret;
+ CamTLConnection *conn = NULL;
+
+ if (tl->connection_ids == 255)
+ return CAM_RETURN_TRANSPORT_TOO_MANY_CONNECTIONS;
+
+ conn = cam_tl_connection_new (tl, ++tl->connection_ids);
+
+ /* send a TAG_CREATE_T_C TPDU */
+ ret = cam_tl_connection_write_control_tpdu (conn, TAG_CREATE_T_C);
+ if (CAM_FAILED (ret))
+ goto error;
+
+ g_hash_table_insert (tl->connections, GINT_TO_POINTER (conn->id), conn);
+
+ *connection = conn;
+
+ return CAM_RETURN_OK;
+
+error:
+ if (conn)
+ cam_tl_connection_destroy (conn);
+
+ return ret;
+}
+
+CamReturn
+cam_tl_connection_delete (CamTLConnection * connection)
+{
+ CamReturn ret;
+
+ ret = cam_tl_connection_write_control_tpdu (connection, TAG_DELETE_T_C);
+ if (CAM_FAILED (ret))
+ return ret;
+
+ connection->state = CAM_TL_CONNECTION_STATE_IN_DELETION;
+
+ return CAM_RETURN_OK;
+}
+
+CamReturn
+handle_control_tpdu (CamTL * tl, CamTLConnection * connection)
+{
+ if (tl->body_length != 0) {
+ GST_ERROR ("got control tpdu of invalid length: %d", tl->body_length);
+ return CAM_RETURN_TRANSPORT_ERROR;
+ }
+
+ switch (tl->buffer[2]) {
+ /* create transport connection reply */
+ case TAG_C_T_C_REPLY:
+ /* a connection might be closed before it's acknowledged */
+ if (connection->state != CAM_TL_CONNECTION_STATE_IN_DELETION) {
+ GST_DEBUG ("connection created %d", connection->id);
+ connection->state = CAM_TL_CONNECTION_STATE_OPEN;
+
+ if (tl->connection_created)
+ tl->connection_created (tl, connection);
+ }
+ break;
+ /* delete transport connection reply */
+ case TAG_D_T_C_REPLY:
+ connection->state = CAM_TL_CONNECTION_STATE_CLOSED;
+ GST_DEBUG ("connection closed %d", connection->id);
+
+ if (tl->connection_deleted)
+ tl->connection_deleted (tl, connection);
+
+ g_hash_table_remove (tl->connections,
+ GINT_TO_POINTER ((guint) connection->id));
+ break;
+ }
+
+ return CAM_RETURN_OK;
+}
+
+CamReturn
+handle_data_tpdu (CamTL * tl, CamTLConnection * connection)
+{
+ if (tl->body_length == 0) {
+ /* FIXME: figure out why this seems to happen from time to time with the
+ * predator cam */
+ GST_WARNING ("Empty data TPDU received");
+ return CAM_RETURN_OK;
+ }
+
+ if (tl->connection_data)
+ return tl->connection_data (tl, connection, tl->body, tl->body_length);
+
+ return CAM_RETURN_OK;
+}
+
+static void
+foreach_connection_get (gpointer key, gpointer value, gpointer user_data)
+{
+ GList **lst = (GList **) user_data;
+
+ *lst = g_list_append (*lst, value);
+}
+
+CamReturn
+cam_tl_connection_poll (CamTLConnection * connection, gboolean force)
+{
+ CamReturn ret;
+
+ if (connection->last_poll == NULL) {
+ connection->last_poll = g_timer_new ();
+ } else if (!force &&
+ g_timer_elapsed (connection->last_poll, NULL) < POLL_INTERVAL) {
+ return CAM_RETURN_TRANSPORT_POLL;
+ }
+
+ GST_DEBUG ("polling connection %d", connection->id);
+ ret = cam_tl_connection_write_control_tpdu (connection, TAG_DATA_LAST);
+ if (CAM_FAILED (ret))
+ return ret;
+
+ g_timer_start (connection->last_poll);
+
+ return CAM_RETURN_OK;
+}
+
+/* read all the queued TPDUs */
+CamReturn
+cam_tl_read_all (CamTL * tl, gboolean poll)
+{
+ CamReturn ret = CAM_RETURN_OK;
+ CamTLConnection *connection;
+ GList *connections = NULL;
+ GList *walk;
+ gboolean done = FALSE;
+
+ while (!done) {
+ while (tl->expected_tpdus) {
+ /* read the next TPDU from the connection */
+ ret = cam_tl_read_tpdu_next (tl, &connection);
+ if (CAM_FAILED (ret)) {
+ GST_ERROR ("error reading TPDU from module: %d", ret);
+ goto out;
+ }
+
+ switch (tl->buffer[2]) {
+ case TAG_C_T_C_REPLY:
+ case TAG_D_T_C_REPLY:
+ connection->empty_data = 0;
+ ret = handle_control_tpdu (tl, connection);
+ break;
+ case TAG_DATA_MORE:
+ case TAG_DATA_LAST:
+ connection->empty_data = 0;
+ ret = handle_data_tpdu (tl, connection);
+ break;
+ case TAG_SB:
+ /* this is handled by tpdu_next */
+ break;
+ }
+
+ if (CAM_FAILED (ret))
+ goto out;
+ }
+
+ done = TRUE;
+
+ connections = NULL;
+ g_hash_table_foreach (tl->connections,
+ foreach_connection_get, &connections);
+
+ for (walk = connections; walk; walk = walk->next) {
+ CamTLConnection *connection = CAM_TL_CONNECTION (walk->data);
+
+ if (connection->has_data == TRUE && connection->empty_data < 10) {
+ ret = cam_tl_connection_write_control_tpdu (connection, TAG_RCV);
+ if (CAM_FAILED (ret)) {
+ g_list_free (connections);
+ goto out;
+ }
+ /* increment the empty_data counter. If we get data, this will be reset
+ * to 0 */
+ connection->empty_data++;
+ done = FALSE;
+ } else if (poll) {
+ ret = cam_tl_connection_poll (connection, FALSE);
+ if (ret == CAM_RETURN_TRANSPORT_POLL)
+ continue;
+
+ if (CAM_FAILED (ret)) {
+ g_list_free (connections);
+ goto out;
+ }
+
+ done = FALSE;
+ }
+ }
+
+ g_list_free (connections);
+ }
+
+out:
+ return ret;
+}
+
+CamReturn
+cam_tl_connection_write (CamTLConnection * connection,
+ guint8 * buffer, guint buffer_size, guint body_length)
+{
+ return cam_tl_connection_write_tpdu (connection,
+ TAG_DATA_LAST, buffer, buffer_size, 1 + body_length);
+}
diff --git a/sys/dvb/camtransport.h b/sys/dvb/camtransport.h
new file mode 100644
index 00000000..570b966c
--- /dev/null
+++ b/sys/dvb/camtransport.h
@@ -0,0 +1,115 @@
+/*
+ * camtransport.h - GStreamer CAM (EN50221) transport layer
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CAM_TRANSPORT_H
+#define CAM_TRANSPORT_H
+
+#include <gst/gst.h>
+#include "cam.h"
+#include "camutils.h"
+
+#define HOST_BUFFER_SIZE 1024
+
+#define CAM_TL(obj) ((CamTL *) obj)
+#define CAM_TL_CONNECTION(obj) ((CamTLConnection *) obj)
+
+typedef struct _CamTL CamTL;
+typedef struct _CamTLPrivate CamTLPrivate;
+
+typedef struct _CamTLConnection CamTLConnection;
+typedef struct _CamTLConnectionPrivate CamTLConnectionPrivate;
+
+typedef enum
+{
+ CAM_TL_CONNECTION_STATE_CLOSED,
+ CAM_TL_CONNECTION_STATE_IN_CREATION,
+ CAM_TL_CONNECTION_STATE_OPEN,
+ CAM_TL_CONNECTION_STATE_IN_DELETION
+} CamTLConnectionState;
+
+struct _CamTL
+{
+ int fd;
+ guint connection_ids;
+
+ GHashTable *connections;
+
+ guint expected_tpdus;
+
+ /* buffer containing module data */
+ guint8 buffer [HOST_BUFFER_SIZE];
+ /* number of bytes written in the buffer */
+ guint buffer_size;
+ /* index pointing to the first byte of a TPDU's body */
+ guint8 *body;
+ /* length of the body part */
+ guint body_length;
+
+ /* callbacks */
+ void (*request_connection) (CamTL *tl, CamTLConnection *connection);
+ void (*connection_created) (CamTL *tl, CamTLConnection *connection);
+ void (*connection_deleted) (CamTL *tl, CamTLConnection *connection);
+ CamReturn (*connection_data) (CamTL *tl, CamTLConnection *connection,
+ guint8 *data, guint length);
+
+ /* used by the upper layer to extend this layer */
+ gpointer user_data;
+};
+
+struct _CamTLConnection
+{
+ CamTL *tl;
+
+ guint8 slot;
+ guint id;
+ CamTLConnectionState state;
+ /* TRUE if the last status byte was 0x80, FALSE otherwise */
+ gboolean has_data;
+ /* NCAS 1.0 sometimes reports that it has data even if it doesn't. After
+ * MAX_EMPTY_DATA times that we don't get any data we assume that there's
+ * actually no data.
+ */
+ guint empty_data;
+ /* timer restarted every time the connection is polled */
+ GTimer *last_poll;
+
+ gpointer user_data;
+};
+
+CamTL *cam_tl_new (int cam_device_fd);
+void cam_tl_destroy (CamTL *tl);
+
+CamReturn cam_tl_create_connection (CamTL *tl,
+ guint8 slot, CamTLConnection **connnection);
+CamReturn cam_tl_connection_delete (CamTLConnection *connection);
+
+void cam_tl_calc_buffer_size (CamTL *tl,
+ guint body_length, guint *buffer_size, guint *offset);
+
+CamReturn cam_tl_connection_write (CamTLConnection *connection,
+ guint8 *data, guint buffer_size, guint body_length);
+
+CamReturn cam_tl_connection_poll (CamTLConnection *connection, gboolean force);
+CamReturn cam_tl_read_all (CamTL *tl, gboolean poll);
+
+#endif /* CAM_TRANSPORT_H */
diff --git a/sys/dvb/camutils.c b/sys/dvb/camutils.c
new file mode 100644
index 00000000..7ca621b4
--- /dev/null
+++ b/sys/dvb/camutils.c
@@ -0,0 +1,340 @@
+/*
+ * camutils.c -
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gst/gst.h>
+#include <string.h>
+
+#include "cam.h"
+#include "camutils.h"
+
+#define GST_CAT_DEFAULT cam_debug_cat
+
+/* From the spec:
+ * length_field() {
+ * size_indicator
+ * if (size_indicator == 0)
+ * length_value
+ * else if (size_indicator == 1) {
+ * length_field_size
+ * for (i=0; i<length_field_size; i++) {
+ * length_value_byte
+ * }
+ * }
+ * }
+*/
+
+guint8
+cam_calc_length_field_size (guint length)
+{
+ guint field_len;
+
+ if (length < G_MAXUINT8)
+ field_len = 1;
+ else if (length <= G_MAXUINT16)
+ field_len = 3;
+ else if (length <= (1 << 24) - 1)
+ field_len = 4;
+ else
+ field_len = 5;
+
+ return field_len;
+}
+
+/* write a length_field */
+guint8
+cam_write_length_field (guint8 * buff, guint length)
+{
+ guint8 field_len = cam_calc_length_field_size (length);
+
+ if (buff) {
+ switch (field_len) {
+ case 1:
+ buff[0] = length;
+ break;
+ case 2:
+ g_return_val_if_reached (0);
+ break;
+ case 3:
+ buff[0] = TPDU_HEADER_SIZE_INDICATOR | (field_len - 1);
+ buff[1] = length >> 8;
+ buff[2] = length & 0xFF;
+ break;
+ case 4:
+ buff[0] = TPDU_HEADER_SIZE_INDICATOR | (field_len - 1);
+ buff[1] = length >> 16;
+ buff[2] = (length >> 8) & 0xFF;
+ buff[3] = length & 0xFF;
+ break;
+ case 5:
+ buff[0] = TPDU_HEADER_SIZE_INDICATOR | (field_len - 1);
+ buff[1] = length >> 24;
+ buff[2] = (length >> 16) & 0xFF;
+ buff[3] = (length >> 8) & 0xFF;
+ buff[4] = length & 0xFF;
+ break;
+ default:
+ g_return_val_if_reached (0);
+ }
+ }
+
+ return field_len;
+}
+
+/* read a length_field */
+guint8
+cam_read_length_field (guint8 * buff, guint * length)
+{
+ guint i;
+ guint field_len;
+ guint8 len;
+
+ if (buff[0] <= G_MAXINT8) {
+ field_len = 1;
+ len = buff[0];
+ } else {
+ field_len = buff[0] & ~TPDU_HEADER_SIZE_INDICATOR;
+ if (field_len > 4) {
+ GST_ERROR ("length_field length exceeds 4 bytes: %d", field_len);
+ field_len = 0;
+ len = 0;
+ } else {
+ len = 0;
+ for (i = 0; i < field_len; ++i)
+ len = (len << 8) | *++buff;
+
+ /* count the size indicator byte */
+ field_len += 1;
+ }
+ }
+
+ if (length)
+ *length = len;
+
+ return field_len;
+}
+
+/*
+ * ca_pmt () {
+ * ca_pmt_tag 24 uimsbf
+ * length_field()
+ * ca_pmt_list_management 8 uimsbf
+ * program_number 16 uimsbf
+ * reserved 2 bslbf
+ * version_number 5 uimsbf
+ * current_next_indicator 1 bslbf
+ * reserved 4 bslbf
+ * program_info_length 12 uimsbf
+ * if (program_info_length != 0) {
+ * ca_pmt_cmd_id at program level 8 uimsbf
+ * for (i=0; i<n; i++) {
+ * CA_descriptor() programme level
+ * }
+ * }
+ * for (i=0; i<n; i++) {
+ * stream_type 8 uimsbf
+ * reserved 3 bslbf
+ * elementary_PID elementary stream PID 13 uimsbf
+ * reserved 4 bslbf
+ * ES_info_length 12 uimsbf
+ * if (ES_info_length != 0) {
+ * ca_pmt_cmd_id at ES level 8 uimsbf
+ * for (i=0; i<n; i++) {
+ * CA_descriptor() elementary stream level
+ * }
+ * }
+ * }
+ * }
+ */
+
+static guint
+get_ca_descriptors_length (GValueArray * descriptors)
+{
+ guint i;
+ guint len = 0;
+ GValue *value;
+ GString *desc;
+
+ if (descriptors != NULL) {
+ for (i = 0; i < descriptors->n_values; ++i) {
+ value = g_value_array_get_nth (descriptors, i);
+ desc = (GString *) g_value_get_boxed (value);
+
+ if (desc->str[0] == 0x09)
+ len += desc->len;
+ }
+ }
+
+ return len;
+}
+
+static guint8 *
+write_ca_descriptors (guint8 * body, GValueArray * descriptors)
+{
+ guint i;
+ GValue *value;
+ GString *desc;
+
+ if (descriptors != NULL) {
+ for (i = 0; i < descriptors->n_values; ++i) {
+ value = g_value_array_get_nth (descriptors, i);
+ desc = (GString *) g_value_get_boxed (value);
+
+ if (desc->str[0] == 0x09) {
+ memcpy (body, desc->str, desc->len);
+ body += desc->len;
+ }
+ }
+ }
+
+ return body;
+}
+
+guint8 *
+cam_build_ca_pmt (GObject * pmt, guint8 list_management, guint8 cmd_id,
+ guint * size)
+{
+ guint body_size = 0;
+ guint8 *buffer;
+ guint8 *body;
+ GList *lengths = NULL;
+ guint len = 0;
+ GValueArray *streams = NULL;
+ gint program_number;
+ guint version_number;
+ guint i;
+ GValue *value;
+ GObject *stream;
+ GValueArray *program_descriptors = NULL;
+ GValueArray *stream_descriptors = NULL;
+
+ g_object_get (pmt,
+ "program-number", &program_number,
+ "version-number", &version_number,
+ "stream-info", &streams, "descriptors", &program_descriptors, NULL);
+
+ /* get the length of program level CA_descriptor()s */
+ len = get_ca_descriptors_length (program_descriptors);
+ if (len > 0)
+ /* add one byte for the program level cmd_id */
+ len += 1;
+
+ lengths = g_list_append (lengths, GINT_TO_POINTER (len));
+ body_size += 6 + len;
+
+ /* get the length of stream level CA_descriptor()s */
+ if (streams != NULL) {
+ for (i = 0; i < streams->n_values; ++i) {
+ value = g_value_array_get_nth (streams, i);
+ stream = g_value_get_object (value);
+
+ g_object_get (stream, "descriptors", &stream_descriptors, NULL);
+
+ len = get_ca_descriptors_length (stream_descriptors);
+ if (len > 0)
+ /* one byte for the stream level cmd_id */
+ len += 1;
+
+ lengths = g_list_append (lengths, GINT_TO_POINTER (len));
+ body_size += 5 + len;
+
+ if (stream_descriptors) {
+ g_value_array_free (stream_descriptors);
+ stream_descriptors = NULL;
+ }
+ }
+ }
+
+ buffer = g_malloc0 (body_size);
+ body = buffer;
+
+ /*
+ guint length_field_len = cam_calc_length_field_size (body_size);
+ guint apdu_header_length = 3 + length_field_len;
+ guint8 *apdu = (buffer + buffer_size) - body_size - apdu_header_length;
+ apdu [0] = 0x9F;
+ apdu [1] = 0x80;
+ apdu [2] = 0x32;
+
+ g_print ("LEN %d %d", length_field_len, body_size);
+
+ cam_write_length_field (&apdu [3], body_size);
+ body += 4;
+ */
+
+ *body++ = list_management;
+
+ GST_WRITE_UINT16_BE (body, program_number);
+ body += 2;
+
+ *body++ = (version_number << 1) | 0x01;
+
+ len = GPOINTER_TO_INT (lengths->data);
+ lengths = g_list_delete_link (lengths, lengths);
+ GST_WRITE_UINT16_BE (body, len);
+ body += 2;
+
+ if (len != 0) {
+ *body++ = cmd_id;
+
+ body = write_ca_descriptors (body, program_descriptors);
+ }
+
+ if (program_descriptors)
+ g_value_array_free (program_descriptors);
+
+ for (i = 0; i < streams->n_values; ++i) {
+ guint stream_type;
+ guint stream_pid;
+
+ value = g_value_array_get_nth (streams, i);
+ stream = g_value_get_object (value);
+
+ g_object_get (stream,
+ "stream-type", &stream_type,
+ "pid", &stream_pid, "descriptors", &stream_descriptors, NULL);
+
+ *body++ = stream_type;
+ GST_WRITE_UINT16_BE (body, stream_pid);
+ body += 2;
+ len = GPOINTER_TO_INT (lengths->data);
+ lengths = g_list_delete_link (lengths, lengths);
+ GST_WRITE_UINT16_BE (body, len);
+ body += 2;
+
+ if (len != 0) {
+ *body++ = cmd_id;
+ body = write_ca_descriptors (body, stream_descriptors);
+ }
+
+ if (stream_descriptors) {
+ g_value_array_free (stream_descriptors);
+ stream_descriptors = NULL;
+ }
+ }
+
+ if (streams)
+ g_value_array_free (streams);
+
+ *size = body_size;
+ return buffer;
+}
diff --git a/sys/dvb/camutils.h b/sys/dvb/camutils.h
new file mode 100644
index 00000000..c63ba947
--- /dev/null
+++ b/sys/dvb/camutils.h
@@ -0,0 +1,59 @@
+/*
+ * camutils.h - GStreamer CAM (EN50221) support
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CAM_UTILS_H
+#define CAM_UTILS_H
+
+#include <glib.h>
+#include <gst/gst.h>
+
+#define TPDU_HEADER_SIZE_INDICATOR 0x80
+
+#define CAM_FAILED(ret) (ret <= CAM_RETURN_ERROR)
+
+typedef enum
+{
+ /* generic */
+ CAM_RETURN_OK = 0,
+ CAM_RETURN_ERROR = -1,
+
+ /* transport specific */
+ CAM_RETURN_TRANSPORT_ERROR = -10,
+ CAM_RETURN_TRANSPORT_TOO_MANY_CONNECTIONS = -11,
+ CAM_RETURN_TRANSPORT_TIMEOUT = -12,
+ CAM_RETURN_TRANSPORT_POLL = -13,
+
+ /* session specific */
+ CAM_RETURN_SESSION_ERROR = -30,
+ CAM_RETURN_SESSION_TOO_MANY_SESSIONS = -31,
+
+ /* application specific */
+ CAM_RETURN_APPLICATION_ERROR = -40,
+} CamReturn;
+
+guint8 cam_calc_length_field_size (guint length);
+guint8 cam_write_length_field (guint8 *buff, guint length);
+guint8 cam_read_length_field (guint8 *buff, guint *length);
+guint8 *cam_build_ca_pmt (GObject *pmt, guint8 list_management, guint8 cmd_id, guint *size);
+
+#endif /* CAM_UTILS_H */
diff --git a/sys/dvb/dvbbasebin.c b/sys/dvb/dvbbasebin.c
new file mode 100644
index 00000000..ad043d63
--- /dev/null
+++ b/sys/dvb/dvbbasebin.c
@@ -0,0 +1,897 @@
+/*
+ * dvbbasebin.c -
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <string.h>
+#include "dvbbasebin.h"
+
+GST_DEBUG_CATEGORY_STATIC (dvb_base_bin_debug);
+#define GST_CAT_DEFAULT dvb_base_bin_debug
+
+static GstElementDetails dvb_base_bin_details = GST_ELEMENT_DETAILS ("DVB bin",
+ "Source/Bin/Video",
+ "Access descramble and split DVB streams",
+ "Alessandro Decina <alessandro@nnva.org>");
+
+static GstStaticPadTemplate src_template =
+GST_STATIC_PAD_TEMPLATE ("src%d", GST_PAD_SRC,
+ GST_PAD_REQUEST,
+ GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
+ );
+
+static GstStaticPadTemplate program_template =
+GST_STATIC_PAD_TEMPLATE ("program_%d", GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
+ );
+
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum
+{
+ ARG_0,
+ PROP_ADAPTER,
+ PROP_FRONTEND,
+ PROP_FREQUENCY,
+ PROP_POLARITY,
+ PROP_SYMBOL_RATE,
+ PROP_BANDWIDTH,
+ PROP_CODE_RATE_HP,
+ PROP_CODE_RATE_LP,
+ PROP_GUARD,
+ PROP_MODULATION,
+ PROP_TRANS_MODE,
+ PROP_HIERARCHY,
+ PROP_INVERSION,
+ PROP_PROGRAM_NUMBERS,
+ PROP_STATS_REPORTING_INTERVAL
+ /* FILL ME */
+};
+
+typedef struct
+{
+ guint16 pid;
+ guint usecount;
+} DvbBaseBinStream;
+
+typedef struct
+{
+ gint program_number;
+ guint16 pmt_pid;
+ guint16 pcr_pid;
+ GObject *pmt;
+ GObject *old_pmt;
+ gboolean selected;
+ gboolean pmt_active;
+ gboolean active;
+ GstPad *ghost;
+} DvbBaseBinProgram;
+
+static void dvb_base_bin_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void dvb_base_bin_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void dvb_base_bin_dispose (GObject * object);
+static void dvb_base_bin_finalize (GObject * object);
+
+static gboolean dvb_base_bin_ts_pad_probe_cb (GstPad * pad,
+ GstBuffer * buf, gpointer user_data);
+static GstStateChangeReturn dvb_base_bin_change_state (GstElement * element,
+ GstStateChange transition);
+static void dvb_base_bin_pat_info_changed_cb (GstElement * mpegtsparse,
+ GParamSpec * pspec, DvbBaseBin * dvbbasebin);
+static void dvb_base_bin_pmt_info_cb (GstElement * mpegtsparse,
+ guint program_number, GObject * pmt, DvbBaseBin * dvbbasebin);
+static void dvb_base_bin_pad_added_cb (GstElement * mpegtsparse,
+ GstPad * pad, DvbBaseBin * dvbbasebin);
+static void dvb_base_bin_pad_removed_cb (GstElement * mpegtsparse,
+ GstPad * pad, DvbBaseBin * dvbbasebin);
+static GstPad *dvb_base_bin_request_new_pad (GstElement * element,
+ GstPadTemplate * templ, const gchar * name);
+static void dvb_base_bin_release_pad (GstElement * element, GstPad * pad);
+
+static DvbBaseBinStream *
+dvb_base_bin_add_stream (DvbBaseBin * dvbbasebin, guint16 pid)
+{
+ DvbBaseBinStream *stream;
+
+ stream = g_new0 (DvbBaseBinStream, 1);
+ stream->pid = pid;
+ stream->usecount = 0;
+
+ g_hash_table_insert (dvbbasebin->streams,
+ GINT_TO_POINTER ((gint) pid), stream);
+
+ return stream;
+}
+
+static DvbBaseBinStream *
+dvb_base_bin_get_stream (DvbBaseBin * dvbbasebin, guint16 pid)
+{
+ return (DvbBaseBinStream *) g_hash_table_lookup (dvbbasebin->streams,
+ GINT_TO_POINTER ((gint) pid));
+}
+
+static DvbBaseBinProgram *
+dvb_base_bin_add_program (DvbBaseBin * dvbbasebin, gint program_number)
+{
+ DvbBaseBinProgram *program;
+
+ program = g_new0 (DvbBaseBinProgram, 1);
+ program->program_number = program_number;
+ program->selected = FALSE;
+ program->active = FALSE;
+ program->pmt_pid = G_MAXUINT16;
+ program->pcr_pid = G_MAXUINT16;
+ program->pmt = NULL;
+ program->old_pmt = NULL;
+
+ g_hash_table_insert (dvbbasebin->programs,
+ GINT_TO_POINTER (program_number), program);
+
+ return program;
+}
+
+static DvbBaseBinProgram *
+dvb_base_bin_get_program (DvbBaseBin * dvbbasebin, gint program_number)
+{
+ return (DvbBaseBinProgram *) g_hash_table_lookup (dvbbasebin->programs,
+ GINT_TO_POINTER (program_number));
+}
+
+/*
+static guint signals [LAST_SIGNAL] = { 0 };
+*/
+
+GST_BOILERPLATE (DvbBaseBin, dvb_base_bin, GstBin, GST_TYPE_BIN);
+
+static void
+dvb_base_bin_base_init (gpointer klass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ element_class->request_new_pad = dvb_base_bin_request_new_pad;
+ element_class->release_pad = dvb_base_bin_release_pad;
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&src_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&program_template));
+
+ gst_element_class_set_details (element_class, &dvb_base_bin_details);
+}
+
+static void
+dvb_base_bin_class_init (DvbBaseBinClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *element_class;
+ GstElementFactory *dvbsrc_factory;
+ GObjectClass *dvbsrc_class;
+ typedef struct
+ {
+ guint prop_id;
+ const gchar *prop_name;
+ } ProxyedProperty;
+ ProxyedProperty *walk;
+ ProxyedProperty proxyed_properties[] = {
+ {PROP_ADAPTER, "adapter"},
+ {PROP_FRONTEND, "frontend"},
+ {PROP_FREQUENCY, "frequency"},
+ {PROP_POLARITY, "polarity"},
+ {PROP_SYMBOL_RATE, "symbol-rate"},
+ {PROP_BANDWIDTH, "bandwidth"},
+ {PROP_CODE_RATE_HP, "code-rate-hp"},
+ {PROP_CODE_RATE_LP, "code-rate-lp"},
+ {PROP_GUARD, "guard"},
+ {PROP_MODULATION, "modulation"},
+ {PROP_TRANS_MODE, "trans-mode"},
+ {PROP_HIERARCHY, "hierarchy"},
+ {PROP_INVERSION, "inversion"},
+ {PROP_STATS_REPORTING_INTERVAL, "stats-reporting-interval"},
+ {0, NULL}
+ };
+
+ element_class = GST_ELEMENT_CLASS (klass);
+ element_class->change_state = dvb_base_bin_change_state;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->set_property = dvb_base_bin_set_property;
+ gobject_class->get_property = dvb_base_bin_get_property;
+ gobject_class->dispose = dvb_base_bin_dispose;
+ gobject_class->finalize = dvb_base_bin_finalize;
+
+ /* install dvbsrc properties */
+ dvbsrc_factory = gst_element_factory_find ("dvbsrc");
+ dvbsrc_class = g_type_class_ref (dvbsrc_factory->type);
+ walk = proxyed_properties;
+ while (walk->prop_name != NULL) {
+ GParamSpec *pspec;
+ GParamSpec *our_pspec;
+
+ pspec = g_object_class_find_property (dvbsrc_class, walk->prop_name);
+ if (pspec != NULL) {
+ GType param_type = G_PARAM_SPEC_TYPE (pspec);
+
+ if (param_type == G_TYPE_PARAM_INT) {
+ GParamSpecInt *src_pspec = G_PARAM_SPEC_INT (pspec);
+
+ our_pspec = g_param_spec_int (g_param_spec_get_name (pspec),
+ g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
+ src_pspec->minimum, src_pspec->maximum, src_pspec->default_value,
+ pspec->flags);
+ } else if (param_type == G_TYPE_PARAM_UINT) {
+ GParamSpecUInt *src_pspec = G_PARAM_SPEC_UINT (pspec);
+
+ our_pspec = g_param_spec_uint (g_param_spec_get_name (pspec),
+ g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
+ src_pspec->minimum, src_pspec->maximum, src_pspec->default_value,
+ pspec->flags);
+ } else if (param_type == G_TYPE_PARAM_STRING) {
+ GParamSpecString *src_pspec = G_PARAM_SPEC_STRING (pspec);
+
+ our_pspec = g_param_spec_string (g_param_spec_get_name (pspec),
+ g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
+ src_pspec->default_value, pspec->flags);
+ } else if (param_type == G_TYPE_PARAM_ENUM) {
+ GParamSpecEnum *src_pspec = G_PARAM_SPEC_ENUM (pspec);
+
+ our_pspec = g_param_spec_enum (g_param_spec_get_name (pspec),
+ g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
+ pspec->value_type, src_pspec->default_value, pspec->flags);
+ } else {
+ g_assert_not_reached ();
+ }
+
+ g_object_class_install_property (gobject_class, walk->prop_id, our_pspec);
+ } else {
+ g_warning ("dvbsrc has no property named %s", walk->prop_name);
+ }
+ ++walk;
+ }
+ g_type_class_unref (dvbsrc_class);
+
+ g_object_class_install_property (gobject_class, PROP_PROGRAM_NUMBERS,
+ g_param_spec_string ("program-numbers",
+ "Program Numbers",
+ "Colon separated list of programs", "", G_PARAM_READWRITE));
+}
+
+static void
+dvb_base_bin_reset (DvbBaseBin * dvbbasebin)
+{
+ if (dvbbasebin->hwcam) {
+ cam_device_close (dvbbasebin->hwcam);
+ cam_device_free (dvbbasebin->hwcam);
+ dvbbasebin->hwcam = NULL;
+ }
+
+ if (dvbbasebin->ts_pad) {
+ gst_element_release_request_pad (GST_ELEMENT (dvbbasebin->mpegtsparse),
+ dvbbasebin->ts_pad);
+ dvbbasebin->ts_pad = NULL;
+ }
+}
+
+static void
+dvb_base_bin_init (DvbBaseBin * dvbbasebin, DvbBaseBinClass * klass)
+{
+ dvbbasebin->dvbsrc = gst_element_factory_make ("dvbsrc", NULL);
+ dvbbasebin->buffer_queue = gst_element_factory_make ("queue", NULL);
+ dvbbasebin->mpegtsparse = gst_element_factory_make ("mpegtsparse", NULL);
+ g_object_connect (dvbbasebin->mpegtsparse,
+ "signal::notify::pat-info", dvb_base_bin_pat_info_changed_cb, dvbbasebin,
+ "signal::pmt-info", dvb_base_bin_pmt_info_cb, dvbbasebin,
+ "signal::pad-added", dvb_base_bin_pad_added_cb, dvbbasebin,
+ "signal::pad-removed", dvb_base_bin_pad_removed_cb, dvbbasebin, NULL);
+
+ gst_bin_add_many (GST_BIN (dvbbasebin), dvbbasebin->dvbsrc,
+ dvbbasebin->buffer_queue, dvbbasebin->mpegtsparse, NULL);
+
+ gst_element_link_many (dvbbasebin->dvbsrc,
+ dvbbasebin->buffer_queue, dvbbasebin->mpegtsparse, NULL);
+
+ dvbbasebin->programs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, g_free);
+ dvbbasebin->streams = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, g_free);
+
+ dvbbasebin->pmtlist = NULL;
+ dvbbasebin->pmtlist_changed = FALSE;
+
+ dvbbasebin->disposed = FALSE;
+ dvb_base_bin_reset (dvbbasebin);
+}
+
+static void
+dvb_base_bin_dispose (GObject * object)
+{
+ DvbBaseBin *dvbbasebin = GST_DVB_BASE_BIN (object);
+
+ if (!dvbbasebin->disposed) {
+ /* remove mpegtsparse BEFORE dvbsrc, since the mpegtsparse::pad-removed
+ * signal handler uses dvbsrc */
+ dvb_base_bin_reset (dvbbasebin);
+ gst_bin_remove (GST_BIN (dvbbasebin), dvbbasebin->mpegtsparse);
+ gst_bin_remove (GST_BIN (dvbbasebin), dvbbasebin->dvbsrc);
+ gst_bin_remove (GST_BIN (dvbbasebin), dvbbasebin->buffer_queue);
+ dvbbasebin->disposed = TRUE;
+ }
+
+ if (G_OBJECT_CLASS (parent_class)->dispose)
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+dvb_base_bin_finalize (GObject * object)
+{
+ DvbBaseBin *dvbbasebin = GST_DVB_BASE_BIN (object);
+
+ g_hash_table_destroy (dvbbasebin->streams);
+ g_hash_table_destroy (dvbbasebin->programs);
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+dvb_base_bin_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ DvbBaseBin *dvbbasebin = GST_DVB_BASE_BIN (object);
+
+ switch (prop_id) {
+ case PROP_ADAPTER:
+ case PROP_FRONTEND:
+ case PROP_FREQUENCY:
+ case PROP_POLARITY:
+ case PROP_SYMBOL_RATE:
+ case PROP_BANDWIDTH:
+ case PROP_CODE_RATE_HP:
+ case PROP_CODE_RATE_LP:
+ case PROP_GUARD:
+ case PROP_MODULATION:
+ case PROP_TRANS_MODE:
+ case PROP_HIERARCHY:
+ case PROP_INVERSION:
+ case PROP_STATS_REPORTING_INTERVAL:
+ /* FIXME: check if we can tune (state < PLAYING || program-numbers == "") */
+ g_object_set_property (G_OBJECT (dvbbasebin->dvbsrc), pspec->name, value);
+ break;
+ case PROP_PROGRAM_NUMBERS:
+ g_object_set_property (G_OBJECT (dvbbasebin->mpegtsparse), pspec->name,
+ value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+dvb_base_bin_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ DvbBaseBin *dvbbasebin = GST_DVB_BASE_BIN (object);
+
+ switch (prop_id) {
+ case PROP_ADAPTER:
+ case PROP_FRONTEND:
+ case PROP_FREQUENCY:
+ case PROP_POLARITY:
+ case PROP_SYMBOL_RATE:
+ case PROP_BANDWIDTH:
+ case PROP_CODE_RATE_HP:
+ case PROP_CODE_RATE_LP:
+ case PROP_GUARD:
+ case PROP_MODULATION:
+ case PROP_TRANS_MODE:
+ case PROP_HIERARCHY:
+ case PROP_INVERSION:
+ case PROP_STATS_REPORTING_INTERVAL:
+ g_object_get_property (G_OBJECT (dvbbasebin->dvbsrc), pspec->name, value);
+ break;
+ case PROP_PROGRAM_NUMBERS:
+ g_object_get_property (G_OBJECT (dvbbasebin->mpegtsparse), pspec->name,
+ value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static GstPad *
+dvb_base_bin_request_new_pad (GstElement * element,
+ GstPadTemplate * templ, const gchar * name)
+{
+ GstPad *pad;
+ GstPad *ghost;
+ gchar *pad_name;
+
+ if (name == NULL)
+ name = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
+
+ pad =
+ gst_element_get_request_pad (GST_DVB_BASE_BIN (element)->mpegtsparse,
+ name);
+ if (pad == NULL)
+ return NULL;
+
+ pad_name = gst_pad_get_name (pad);
+ ghost = gst_ghost_pad_new (pad_name, pad);
+ g_free (pad_name);
+ gst_element_add_pad (element, ghost);
+
+ return ghost;
+}
+
+static void
+dvb_base_bin_release_pad (GstElement * element, GstPad * pad)
+{
+ GstGhostPad *ghost;
+ GstPad *target;
+
+ g_return_if_fail (GST_IS_DVB_BASE_BIN (element));
+
+ ghost = GST_GHOST_PAD (pad);
+ target = gst_ghost_pad_get_target (ghost);
+ gst_element_release_request_pad (GST_ELEMENT (GST_DVB_BASE_BIN (element)->
+ mpegtsparse), target);
+ gst_object_unref (target);
+
+ gst_element_remove_pad (element, pad);
+}
+
+static void
+dvb_base_bin_reset_pmtlist (DvbBaseBin * dvbbasebin)
+{
+ CamConditionalAccessPmtFlag flag;
+ GList *walk;
+ GObject *pmt;
+
+ walk = dvbbasebin->pmtlist;
+ while (walk) {
+ if (walk->prev == NULL) {
+ if (walk->next == NULL)
+ flag = CAM_CONDITIONAL_ACCESS_PMT_FLAG_ONLY;
+ else
+ flag = CAM_CONDITIONAL_ACCESS_PMT_FLAG_FIRST;
+ } else {
+ if (walk->next == NULL)
+ flag = CAM_CONDITIONAL_ACCESS_PMT_FLAG_LAST;
+ else
+ flag = CAM_CONDITIONAL_ACCESS_PMT_FLAG_MORE;
+ }
+
+ pmt = G_OBJECT (walk->data);
+ cam_device_set_pmt (dvbbasebin->hwcam, pmt, flag);
+
+ walk = walk->next;
+ }
+
+ dvbbasebin->pmtlist_changed = FALSE;
+}
+
+static gboolean
+dvb_base_bin_ts_pad_probe_cb (GstPad * pad, GstBuffer * buf, gpointer user_data)
+{
+ DvbBaseBin *dvbbasebin = GST_DVB_BASE_BIN (user_data);
+
+ if (dvbbasebin->hwcam) {
+ cam_device_poll (dvbbasebin->hwcam);
+
+ if (dvbbasebin->pmtlist_changed) {
+ if (cam_device_ready (dvbbasebin->hwcam)) {
+ GST_DEBUG_OBJECT (dvbbasebin, "pmt list changed");
+ dvb_base_bin_reset_pmtlist (dvbbasebin);
+ } else {
+ GST_DEBUG_OBJECT (dvbbasebin, "pmt list changed but CAM not ready");
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+dvb_base_bin_init_cam (DvbBaseBin * dvbbasebin)
+{
+ gint adapter;
+ gchar *ca_file;
+
+ g_object_get (dvbbasebin->dvbsrc, "adapter", &adapter, NULL);
+ /* TODO: handle multiple cams */
+ ca_file = g_strdup_printf ("/dev/dvb/adapter%d/ca0", adapter);
+ if (g_file_test (ca_file, G_FILE_TEST_EXISTS)) {
+ dvbbasebin->hwcam = cam_device_new ();
+ if (cam_device_open (dvbbasebin->hwcam, ca_file)) {
+ /* HACK: poll the cam in a buffer probe */
+ dvbbasebin->ts_pad =
+ gst_element_get_request_pad (dvbbasebin->mpegtsparse, "src%d");
+ gst_pad_add_buffer_probe (dvbbasebin->ts_pad,
+ G_CALLBACK (dvb_base_bin_ts_pad_probe_cb), dvbbasebin);
+ } else {
+ GST_ERROR_OBJECT (dvbbasebin, "could not open %s", ca_file);
+ cam_device_free (dvbbasebin->hwcam);
+ dvbbasebin->hwcam = NULL;
+ }
+ }
+
+ g_free (ca_file);
+}
+
+static GstStateChangeReturn
+dvb_base_bin_change_state (GstElement * element, GstStateChange transition)
+{
+ DvbBaseBin *dvbbasebin;
+ GstStateChangeReturn ret;
+
+ dvbbasebin = GST_DVB_BASE_BIN (element);
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ dvb_base_bin_init_cam (dvbbasebin);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ dvb_base_bin_reset (dvbbasebin);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+foreach_stream_build_filter (gpointer key, gpointer value, gpointer user_data)
+{
+ DvbBaseBin *dvbbasebin = GST_DVB_BASE_BIN (user_data);
+ DvbBaseBinStream *stream = (DvbBaseBinStream *) value;
+ gchar *tmp, *pid;
+
+ g_assert (stream->usecount >= 0);
+
+ GST_DEBUG ("stream %d usecount %d", stream->pid, stream->usecount);
+
+ if (stream->usecount > 0) {
+ /* TODO: use g_strjoinv FTW */
+ tmp = dvbbasebin->filter;
+ pid = g_strdup_printf ("%d", stream->pid);
+ dvbbasebin->filter = g_strjoin (":", pid, dvbbasebin->filter, NULL);
+
+ g_free (pid);
+ g_free (tmp);
+ }
+}
+
+static void
+dvb_base_bin_rebuild_filter (DvbBaseBin * dvbbasebin)
+{
+ g_hash_table_foreach (dvbbasebin->streams,
+ foreach_stream_build_filter, dvbbasebin);
+
+ if (dvbbasebin->filter == NULL)
+ /* fix dvbsrc to handle NULL filter */
+ dvbbasebin->filter = g_strdup ("");
+
+ GST_INFO_OBJECT (dvbbasebin, "rebuilt filter %s", dvbbasebin->filter);
+
+ /* FIXME: disable this until we find a way to filter out unwanted pids
+ g_object_set (dvbbasebin->dvbsrc, "pids", dvbbasebin->filter, NULL);
+ */
+ g_free (dvbbasebin->filter);
+ dvbbasebin->filter = NULL;
+}
+
+static void
+dvb_base_bin_remove_pmt_streams (DvbBaseBin * dvbbasebin, GObject * pmt)
+{
+ GValueArray *streams;
+ gint program_number;
+ gint i;
+ GValue *value;
+ GObject *streamobj;
+ DvbBaseBinStream *stream;
+ guint pid;
+ guint stream_type;
+
+ g_object_get (pmt, "program-number", &program_number,
+ "stream-info", &streams, NULL);
+
+ for (i = 0; i < streams->n_values; ++i) {
+ value = g_value_array_get_nth (streams, i);
+ streamobj = g_value_get_object (value);
+
+ g_object_get (streamobj, "pid", &pid, "stream-type", &stream_type, NULL);
+
+ stream = dvb_base_bin_get_stream (dvbbasebin, (guint16) pid);
+ if (stream == NULL) {
+ GST_WARNING_OBJECT (dvbbasebin, "removing unknown stream %d ??", pid);
+ continue;
+ }
+
+ --stream->usecount;
+ }
+}
+
+static void
+dvb_base_bin_add_pmt_streams (DvbBaseBin * dvbbasebin, GObject * pmt)
+{
+ DvbBaseBinStream *stream;
+ GValueArray *streams;
+ gint program_number;
+ gint i;
+ GValue *value;
+ GObject *streamobj;
+ guint pid;
+ guint stream_type;
+
+ g_object_get (pmt, "program-number", &program_number,
+ "stream-info", &streams, NULL);
+
+ for (i = 0; i < streams->n_values; ++i) {
+ value = g_value_array_get_nth (streams, i);
+ streamobj = g_value_get_object (value);
+
+ g_object_get (streamobj, "pid", &pid, "stream-type", &stream_type, NULL);
+ GST_DEBUG ("filtering stream %d stream_type %d", pid, stream_type);
+
+ stream = dvb_base_bin_get_stream (dvbbasebin, (guint16) pid);
+ if (stream == NULL)
+ stream = dvb_base_bin_add_stream (dvbbasebin, (guint16) pid);
+
+ ++stream->usecount;
+ }
+}
+
+static void
+dvb_base_bin_activate_program (DvbBaseBin * dvbbasebin,
+ DvbBaseBinProgram * program)
+{
+ DvbBaseBinStream *stream;
+ guint pid;
+
+ if (program->old_pmt) {
+ dvb_base_bin_remove_pmt_streams (dvbbasebin, program->old_pmt);
+ dvbbasebin->pmtlist = g_list_remove (dvbbasebin->pmtlist, program->old_pmt);
+ }
+
+ /* activate the PMT and PCR streams. If the PCR stream is in the PMT its
+ * usecount will be incremented by 2 here and decremented by 2 when the
+ * program is deactivated */
+ if (!program->pmt_active) {
+ stream = dvb_base_bin_get_stream (dvbbasebin, program->pmt_pid);
+ if (stream == NULL)
+ stream = dvb_base_bin_add_stream (dvbbasebin, program->pmt_pid);
+ stream->usecount += 1;
+ program->pmt_active = TRUE;
+ }
+
+ if (program->pmt) {
+ guint16 old_pcr_pid;
+
+ old_pcr_pid = program->pcr_pid;
+ g_object_get (program->pmt, "pcr-pid", &pid, NULL);
+ program->pcr_pid = pid;
+ if (old_pcr_pid != G_MAXUINT16 && old_pcr_pid != program->pcr_pid)
+ dvb_base_bin_get_stream (dvbbasebin, old_pcr_pid)->usecount--;
+
+ stream = dvb_base_bin_get_stream (dvbbasebin, program->pcr_pid);
+ if (stream == NULL)
+ stream = dvb_base_bin_add_stream (dvbbasebin, program->pcr_pid);
+ stream->usecount += 1;
+
+ dvb_base_bin_add_pmt_streams (dvbbasebin, program->pmt);
+ dvbbasebin->pmtlist = g_list_append (dvbbasebin->pmtlist, program->pmt);
+ dvbbasebin->pmtlist_changed = TRUE;
+ program->active = TRUE;
+ }
+
+ dvb_base_bin_rebuild_filter (dvbbasebin);
+}
+
+static void
+dvb_base_bin_deactivate_program (DvbBaseBin * dvbbasebin,
+ DvbBaseBinProgram * program)
+{
+ DvbBaseBinStream *stream;
+
+ stream = dvb_base_bin_get_stream (dvbbasebin, program->pmt_pid);
+ if (stream != NULL)
+ stream->usecount -= 1;
+
+ stream = dvb_base_bin_get_stream (dvbbasebin, program->pcr_pid);
+ if (stream != NULL)
+ stream->usecount -= 1;
+
+ if (program->pmt) {
+ dvb_base_bin_remove_pmt_streams (dvbbasebin, program->pmt);
+ dvbbasebin->pmtlist = g_list_remove (dvbbasebin->pmtlist, program->pmt);
+ dvbbasebin->pmtlist_changed = TRUE;
+ }
+
+ dvb_base_bin_rebuild_filter (dvbbasebin);
+ program->pmt_active = FALSE;
+ program->active = FALSE;
+}
+
+static void
+dvb_base_bin_pat_info_changed_cb (GstElement * mpegtsparse,
+ GParamSpec * pspec, DvbBaseBin * dvbbasebin)
+{
+ DvbBaseBinProgram *program;
+ DvbBaseBinStream *stream;
+ GValueArray *pat_info;
+ GValue *value;
+ GObject *program_info;
+ gint program_number;
+ guint pid;
+ guint16 old_pmt_pid;
+ gint i;
+ gboolean rebuild_filter = FALSE;
+
+ g_object_get (mpegtsparse, "pat-info", &pat_info, NULL);
+
+ for (i = 0; i < pat_info->n_values; ++i) {
+ value = g_value_array_get_nth (pat_info, i);
+ program_info = g_value_get_object (value);
+
+ g_object_get (program_info, "program-number", &program_number,
+ "pid", &pid, NULL);
+
+ program = dvb_base_bin_get_program (dvbbasebin, program_number);
+ if (program == NULL)
+ program = dvb_base_bin_add_program (dvbbasebin, program_number);
+
+ old_pmt_pid = program->pmt_pid;
+ program->pmt_pid = pid;
+
+ if (program->selected) {
+ /* PAT update */
+ if (old_pmt_pid != G_MAXUINT16 && old_pmt_pid != program->pmt_pid)
+ dvb_base_bin_get_stream (dvbbasebin, old_pmt_pid)->usecount -= 1;
+
+ stream = dvb_base_bin_get_stream (dvbbasebin, program->pmt_pid);
+ if (stream == NULL)
+ stream = dvb_base_bin_add_stream (dvbbasebin, program->pmt_pid);
+
+ stream->usecount += 1;
+
+ rebuild_filter = TRUE;
+ }
+ }
+
+ g_value_array_free (pat_info);
+
+ if (rebuild_filter)
+ dvb_base_bin_rebuild_filter (dvbbasebin);
+}
+
+static void
+dvb_base_bin_pmt_info_cb (GstElement * mpegtsparse,
+ guint program_number, GObject * pmt, DvbBaseBin * dvbbasebin)
+{
+ DvbBaseBinProgram *program;
+
+ program = dvb_base_bin_get_program (dvbbasebin, program_number);
+ if (program == NULL) {
+ GST_WARNING ("got PMT for program %d but program not in PAT",
+ program_number);
+ program = dvb_base_bin_add_program (dvbbasebin, program_number);
+ }
+
+ program->old_pmt = program->pmt;
+ program->pmt = g_object_ref (pmt);
+
+ /* activate the program if it's selected and either it's not active or its pmt
+ * changed */
+ if (program->selected && (!program->active || program->old_pmt != NULL))
+ dvb_base_bin_activate_program (dvbbasebin, program);
+
+ if (program->old_pmt) {
+ g_object_unref (program->old_pmt);
+ program->old_pmt = NULL;
+ }
+}
+
+static gint
+get_pad_program_number (GstPad * pad)
+{
+ gchar *progstr;
+ gchar *name;
+
+ name = gst_pad_get_name (pad);
+
+ if (strncmp (name, "program_", 8) != 0) {
+ g_free (name);
+ return -1;
+ }
+
+ progstr = strstr (name, "_");
+ g_free (name);
+ if (progstr == NULL)
+ return -1;
+
+ return strtol (++progstr, NULL, 10);
+}
+
+static void
+dvb_base_bin_pad_added_cb (GstElement * mpegtsparse,
+ GstPad * pad, DvbBaseBin * dvbbasebin)
+{
+ DvbBaseBinProgram *program;
+ gint program_number;
+
+ program_number = get_pad_program_number (pad);
+ if (program_number == -1)
+ return;
+
+ program = dvb_base_bin_get_program (dvbbasebin, program_number);
+ if (program == NULL)
+ program = dvb_base_bin_add_program (dvbbasebin, program_number);
+ program->selected = TRUE;
+ program->ghost = gst_ghost_pad_new (gst_pad_get_name (pad), pad);
+ gst_pad_set_active (program->ghost, TRUE);
+ gst_element_add_pad (GST_ELEMENT (dvbbasebin), program->ghost);
+
+ /* if the program has a pmt, activate it now, otherwise it will get activated
+ * when there's a PMT */
+ if (!program->active && program->pmt_pid != G_MAXUINT16)
+ dvb_base_bin_activate_program (dvbbasebin, program);
+}
+
+static void
+dvb_base_bin_pad_removed_cb (GstElement * mpegtsparse,
+ GstPad * pad, DvbBaseBin * dvbbasebin)
+{
+ DvbBaseBinProgram *program;
+ gint program_number;
+
+ program_number = get_pad_program_number (pad);
+ if (program_number == -1)
+ return;
+
+ program = dvb_base_bin_get_program (dvbbasebin, program_number);
+ program->selected = FALSE;
+ dvb_base_bin_deactivate_program (dvbbasebin, program);
+ gst_element_remove_pad (GST_ELEMENT (dvbbasebin), program->ghost);
+ program->ghost = NULL;
+}
+
+gboolean
+gst_dvb_base_bin_plugin_init (GstPlugin * plugin)
+{
+ GST_DEBUG_CATEGORY_INIT (dvb_base_bin_debug, "dvbbasebin", 0, "DVB bin");
+
+ cam_init ();
+
+ return gst_element_register (plugin, "dvbbasebin",
+ GST_RANK_NONE, GST_TYPE_DVB_BASE_BIN);
+}
diff --git a/sys/dvb/dvbbasebin.h b/sys/dvb/dvbbasebin.h
new file mode 100644
index 00000000..fbf0b189
--- /dev/null
+++ b/sys/dvb/dvbbasebin.h
@@ -0,0 +1,75 @@
+/*
+ * dvbbasebin.h -
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GST_DVB_BASE_BIN_H
+#define GST_DVB_BASE_BIN_H
+
+#include <gst/gst.h>
+#include <glib.h>
+#include "camdevice.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_DVB_BASE_BIN \
+ (dvb_base_bin_get_type())
+#define GST_DVB_BASE_BIN(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DVB_BASE_BIN,DvbBaseBin))
+#define GST_DVB_BASE_BIN_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DVB_BASE_BIN,DvbBaseBinClass))
+#define GST_IS_DVB_BASE_BIN(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DVB_BASE_BIN))
+#define GST_IS_DVB_BASE_BIN_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DVB_BASE_BIN))
+
+typedef struct _DvbBaseBin DvbBaseBin;
+typedef struct _DvbBaseBinClass DvbBaseBinClass;
+
+struct _DvbBaseBin {
+ GstBin bin;
+
+ GstPad *ts_pad;
+
+ GstElement *dvbsrc;
+ GstElement *buffer_queue;
+ GstElement *mpegtsparse;
+ CamDevice *hwcam;
+ GList *pmtlist;
+ gboolean pmtlist_changed;
+ gchar *filter;
+ GHashTable *streams;
+ GHashTable *programs;
+ gboolean disposed;
+};
+
+struct _DvbBaseBinClass {
+ GstBinClass parent_class;
+
+ /* signals */
+};
+
+GType gst_dvb_base_bin_get_type(void);
+gboolean gst_dvb_base_bin_plugin_init (GstPlugin *plugin);
+
+G_END_DECLS
+
+#endif /* GST_DVB_BASE_BIN_H */
diff --git a/sys/dvb/gstdvb.c b/sys/dvb/gstdvb.c
new file mode 100644
index 00000000..1087173e
--- /dev/null
+++ b/sys/dvb/gstdvb.c
@@ -0,0 +1,46 @@
+/*
+ * gstdvb.c -
+ * Copyright (C) 2007 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstdvbsrc.h"
+#include "dvbbasebin.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ if (!gst_dvbsrc_plugin_init (plugin))
+ return FALSE;
+
+ if (!gst_dvb_base_bin_plugin_init (plugin))
+ return FALSE;
+
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "dvb",
+ "DVB elements",
+ plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/sys/dvb/gstdvbsrc.c b/sys/dvb/gstdvbsrc.c
index f72ed704..84594527 100644
--- a/sys/dvb/gstdvbsrc.c
+++ b/sys/dvb/gstdvbsrc.c
@@ -105,7 +105,8 @@ enum
ARG_DVBSRC_TRANSMISSION_MODE,
ARG_DVBSRC_HIERARCHY_INF,
ARG_DVBSRC_TUNE,
- ARG_DVBSRC_INVERSION
+ ARG_DVBSRC_INVERSION,
+ ARG_DVBSRC_STATS_REPORTING_INTERVAL
};
static void gst_dvbsrc_output_frontend_stats (GstDvbSrc * src);
@@ -246,9 +247,9 @@ gst_dvbsrc_inversion_get_type (void)
{
static GType dvbsrc_inversion_type = 0;
static GEnumValue inversion_types[] = {
- {INVERSION_AUTO, "AUTO", "AUTO"},
+ {INVERSION_OFF, "OFF", "OFF"},
{INVERSION_ON, "ON", "ON"},
- {INVERSION_AUTO, "OFF", "OFF"},
+ {INVERSION_AUTO, "AUTO", "AUTO"},
{0, NULL, NULL},
};
@@ -384,50 +385,57 @@ gst_dvbsrc_class_init (GstDvbSrcClass * klass)
g_object_class_install_property (gobject_class, ARG_DVBSRC_BANDWIDTH,
g_param_spec_enum ("bandwidth",
"bandwidth",
- "Bandwidth (DVB-T)", GST_TYPE_DVBSRC_BANDWIDTH, 1, G_PARAM_WRITABLE));
+ "Bandwidth (DVB-T)", GST_TYPE_DVBSRC_BANDWIDTH, 1,
+ G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, ARG_DVBSRC_CODE_RATE_HP,
g_param_spec_enum ("code-rate-hp",
"code-rate-hp",
"High Priority Code Rate (DVB-T)",
- GST_TYPE_DVBSRC_CODE_RATE, 1, G_PARAM_WRITABLE));
+ GST_TYPE_DVBSRC_CODE_RATE, 1, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, ARG_DVBSRC_CODE_RATE_LP,
g_param_spec_enum ("code-rate-lp",
"code-rate-lp",
"Low Priority Code Rate (DVB-T)",
- GST_TYPE_DVBSRC_CODE_RATE, 1, G_PARAM_WRITABLE));
+ GST_TYPE_DVBSRC_CODE_RATE, 1, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, ARG_DVBSRC_GUARD,
g_param_spec_enum ("guard",
"guard",
"Guard Interval (DVB-T)",
- GST_TYPE_DVBSRC_GUARD, 1, G_PARAM_WRITABLE));
+ GST_TYPE_DVBSRC_GUARD, 1, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, ARG_DVBSRC_MODULATION,
g_param_spec_enum ("modulation",
"modulation",
"Modulation (DVB-T)",
- GST_TYPE_DVBSRC_MODULATION, 1, G_PARAM_WRITABLE));
+ GST_TYPE_DVBSRC_MODULATION, 1, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
ARG_DVBSRC_TRANSMISSION_MODE,
g_param_spec_enum ("trans-mode",
"trans-mode",
"Transmission Mode (DVB-T)",
- GST_TYPE_DVBSRC_TRANSMISSION_MODE, 1, G_PARAM_WRITABLE));
+ GST_TYPE_DVBSRC_TRANSMISSION_MODE, 1, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, ARG_DVBSRC_HIERARCHY_INF,
g_param_spec_enum ("hierarchy",
"hierarchy",
"Hierarchy Information (DVB-T)",
- GST_TYPE_DVBSRC_HIERARCHY, 1, G_PARAM_WRITABLE));
+ GST_TYPE_DVBSRC_HIERARCHY, 1, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, ARG_DVBSRC_INVERSION,
g_param_spec_enum ("inversion",
"inversion",
"Inversion Information (DVB-T)",
- GST_TYPE_DVBSRC_INVERSION, 1, G_PARAM_WRITABLE));
+ GST_TYPE_DVBSRC_INVERSION, 1, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ ARG_DVBSRC_STATS_REPORTING_INTERVAL,
+ g_param_spec_uint ("stats-reporting-interval",
+ "stats-reporting-interval",
+ "The number of reads before reporting frontend stats",
+ 0, G_MAXUINT, 100, G_PARAM_READWRITE));
}
/* initialize the new element
@@ -449,7 +457,7 @@ gst_dvbsrc_init (GstDvbSrc * object, GstDvbSrcClass * klass)
object->fd_dvr = -1;
for (i = 0; i < MAX_FILTERS; i++) {
- object->pids[i] = 0;
+ object->pids[i] = G_MAXUINT16;
object->fd_filters[i] = -1;
}
/* Pid 8192 on DVB gets the whole transport stream */
@@ -504,32 +512,44 @@ gst_dvbsrc_set_property (GObject * _object, guint prop_id,
break;
case ARG_DVBSRC_PIDS:
{
- int pid = 0;
- int pid_count = 0;
gchar *pid_string;
- gchar **pids;
- char **tmp;
GST_INFO_OBJECT (object, "Set Property: ARG_DVBSRC_PIDS");
pid_string = g_value_dup_string (value);
- tmp = pids = g_strsplit (pid_string, ":", MAX_FILTERS);
- while (*pids != NULL && pid_count < MAX_FILTERS) {
- pid = strtol (*pids, NULL, 0);
- if (pid > 0 && pid <= 8192) {
- GST_INFO_OBJECT (object, "Parsed Pid: %d\n", pid);
- object->pids[pid_count] = pid;
- pid_count++;
+ if (!strcmp (pid_string, "8192")) {
+ /* get the whole ts */
+ object->pids[0] = 8192;
+ } else {
+ int pid = 0;
+ int pid_count;
+ gchar **pids;
+ char **tmp;
+
+ tmp = pids = g_strsplit (pid_string, ":", MAX_FILTERS);
+
+ /* always add the PAT and CAT pids */
+ object->pids[0] = 0;
+ object->pids[1] = 1;
+
+ pid_count = 2;
+ while (*pids != NULL && pid_count < MAX_FILTERS) {
+ pid = strtol (*pids, NULL, 0);
+ if (pid > 1 && pid <= 8192) {
+ GST_INFO_OBJECT (object, "Parsed Pid: %d\n", pid);
+ object->pids[pid_count] = pid;
+ pid_count++;
+ }
+ pids++;
}
- pids++;
+
+ g_strfreev (tmp);
}
- g_strfreev (tmp);
/* if we are in playing, then set filters now */
GST_INFO_OBJECT (object, "checking if playing for setting pes filters");
if (GST_ELEMENT (object)->current_state == GST_STATE_PLAYING) {
GST_INFO_OBJECT (object, "Setting pes filters now");
gst_dvbsrc_set_pes_filters (object);
}
-
}
break;
case ARG_DVBSRC_SYM_RATE:
@@ -573,6 +593,10 @@ gst_dvbsrc_set_property (GObject * _object, guint prop_id,
g_mutex_unlock (object->tune_mutex);
}
break;
+ case ARG_DVBSRC_STATS_REPORTING_INTERVAL:
+ object->stats_interval = g_value_get_uint (value);
+ object->stats_counter = 0;
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -610,6 +634,33 @@ gst_dvbsrc_get_property (GObject * _object, guint prop_id,
case ARG_DVBSRC_DISEQC_SRC:
g_value_set_int (value, object->diseqc_src);
break;
+ case ARG_DVBSRC_BANDWIDTH:
+ g_value_set_enum (value, object->bandwidth);
+ break;
+ case ARG_DVBSRC_CODE_RATE_HP:
+ g_value_set_enum (value, object->code_rate_hp);
+ break;
+ case ARG_DVBSRC_CODE_RATE_LP:
+ g_value_set_enum (value, object->code_rate_lp);
+ break;
+ case ARG_DVBSRC_GUARD:
+ g_value_set_enum (value, object->guard_interval);
+ break;
+ case ARG_DVBSRC_MODULATION:
+ g_value_set_enum (value, object->modulation);
+ break;
+ case ARG_DVBSRC_TRANSMISSION_MODE:
+ g_value_set_enum (value, object->transmission_mode);
+ break;
+ case ARG_DVBSRC_HIERARCHY_INF:
+ g_value_set_enum (value, object->hierarchy_information);
+ break;
+ case ARG_DVBSRC_INVERSION:
+ g_value_set_enum (value, object->inversion);
+ break;
+ case ARG_DVBSRC_STATS_REPORTING_INTERVAL:
+ g_value_set_uint (value, object->stats_interval);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -737,6 +788,9 @@ gst_dvbsrc_finalize (GObject * _object)
/* freeing the mutex segfaults somehow */
g_mutex_free (object->tune_mutex);
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (_object);
}
@@ -755,8 +809,8 @@ gst_dvbsrc_finalize (GObject * _object)
* register the element factories and pad templates
* register the features
*/
-static gboolean
-plugin_init (GstPlugin * plugin)
+gboolean
+gst_dvbsrc_plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (gstdvbsrc_debug, "dvbsrc", 0, "DVB Source Element");
@@ -770,12 +824,6 @@ plugin_init (GstPlugin * plugin)
GST_TYPE_DVBSRC);
}
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "dvbsrc",
- "DVB Source", plugin_init, VERSION, "LGPL", "", "University of Paderborn");
-
-
static GstBuffer *
read_device (int fd, int adapter_number, int frontend_number, int size)
{
@@ -843,7 +891,6 @@ read_device (int fd, int adapter_number, int frontend_number, int size)
static GstFlowReturn
gst_dvbsrc_create (GstPushSrc * element, GstBuffer ** buf)
{
- static int quality_signal_rate = 0; /* FIXME: move into object struct? */
gint buffer_size;
GstFlowReturn retval = GST_FLOW_ERROR;
GstDvbSrc *object;
@@ -871,17 +918,15 @@ gst_dvbsrc_create (GstPushSrc * element, GstBuffer ** buf)
caps = gst_pad_get_caps (GST_BASE_SRC_PAD (object));
gst_buffer_set_caps (*buf, caps);
gst_caps_unref (caps);
-
- /* Every now and then signal signal quality */
- if (quality_signal_rate == 100) {
- gst_dvbsrc_output_frontend_stats (object);
- quality_signal_rate = 0;
- } else {
- quality_signal_rate++;
- }
} else {
GST_DEBUG_OBJECT (object, "Failed to read from device");
}
+
+ if (object->stats_interval != 0 &&
+ ++object->stats_counter == object->stats_interval) {
+ gst_dvbsrc_output_frontend_stats (object);
+ object->stats_counter = 0;
+ }
}
g_mutex_unlock (object->tune_mutex);
@@ -1188,7 +1233,7 @@ gst_dvbsrc_set_pes_filters (GstDvbSrc * object)
GST_INFO_OBJECT (object, "Setting PES filter");
for (i = 0; i < MAX_FILTERS; i++) {
- if (object->pids[i] == 0)
+ if (object->pids[i] == G_MAXUINT16)
break;
fd = &object->fd_filters[i];
@@ -1214,29 +1259,6 @@ gst_dvbsrc_set_pes_filters (GstDvbSrc * object)
GST_WARNING_OBJECT (object, "Error setting PES filter on %s: %s",
demux_dev, strerror (errno));
}
- /* always have PAT in the filter if we haven't used all our filter slots */
- if (object->pids[0] != 8192 && i < MAX_FILTERS) {
- /* pid 8192 means get whole ts */
- pes_filter.pid = 0;
- pes_filter.input = DMX_IN_FRONTEND;
- pes_filter.output = DMX_OUT_TS_TAP;
- pes_filter.pes_type = DMX_PES_OTHER;
- pes_filter.flags = DMX_IMMEDIATE_START;
- fd = &object->fd_filters[i];
- close (*fd);
- if ((*fd = open (demux_dev, O_RDWR)) < 0) {
- GST_WARNING_OBJECT ("Error opening demuxer: %s (%s)",
- strerror (errno), demux_dev);
- } else {
- GST_INFO_OBJECT (object, "Setting pes-filter, pid = %d, type = %d",
- pes_filter.pid, pes_filter.pes_type);
-
- if (ioctl (*fd, DMX_SET_PES_FILTER, &pes_filter) < 0)
- GST_WARNING_OBJECT (object, "Error setting PES filter on %s: %s",
- demux_dev, strerror (errno));
- }
- }
g_free (demux_dev);
-
}
diff --git a/sys/dvb/gstdvbsrc.h b/sys/dvb/gstdvbsrc.h
index 46b301c1..8e6640ed 100644
--- a/sys/dvb/gstdvbsrc.h
+++ b/sys/dvb/gstdvbsrc.h
@@ -24,7 +24,7 @@ G_BEGIN_DECLS
#define DEFAULT_BUFFER_SIZE 8192
#define DEFAULT_DISEQC_SRC -1 /* disabled */
-#define MAX_FILTERS 8
+#define MAX_FILTERS 32
/* #define's don't like whitespacey bits */
#define GST_TYPE_DVBSRC \
@@ -76,6 +76,8 @@ G_BEGIN_DECLS
int inversion;
GstDvbSrcPol pol;
+ guint stats_interval;
+ guint stats_counter;
};
struct _GstDvbSrcClass
@@ -88,6 +90,7 @@ G_BEGIN_DECLS
GType gst_dvbsrc_get_type (void);
+ gboolean gst_dvbsrc_plugin_init (GstPlugin *plugin);
G_END_DECLS