diff options
author | Alessandro Decina <alessandro@nnva.org> | 2007-10-16 17:38:05 +0000 |
---|---|---|
committer | Zaheer Abbas Merali <zaheerabbas@merali.org> | 2007-10-16 17:38:05 +0000 |
commit | c205b740d4e793354493c4a2663e45670255453c (patch) | |
tree | 3c5d8978e68cf860b69c279e14372ccc144a883c /sys | |
parent | 389904f6248c65468175c072441747f51c5a5296 (diff) | |
download | gst-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')
-rw-r--r-- | sys/dvb/Makefile.am | 39 | ||||
-rw-r--r-- | sys/dvb/cam.c | 33 | ||||
-rw-r--r-- | sys/dvb/cam.h | 33 | ||||
-rw-r--r-- | sys/dvb/camapplication.c | 292 | ||||
-rw-r--r-- | sys/dvb/camapplication.h | 84 | ||||
-rw-r--r-- | sys/dvb/camapplicationinfo.c | 153 | ||||
-rw-r--r-- | sys/dvb/camapplicationinfo.h | 41 | ||||
-rw-r--r-- | sys/dvb/camconditionalaccess.c | 208 | ||||
-rw-r--r-- | sys/dvb/camconditionalaccess.h | 55 | ||||
-rw-r--r-- | sys/dvb/camdevice.c | 213 | ||||
-rw-r--r-- | sys/dvb/camdevice.h | 69 | ||||
-rw-r--r-- | sys/dvb/camresourcemanager.c | 192 | ||||
-rw-r--r-- | sys/dvb/camresourcemanager.h | 41 | ||||
-rw-r--r-- | sys/dvb/camsession.c | 595 | ||||
-rw-r--r-- | sys/dvb/camsession.h | 96 | ||||
-rw-r--r-- | sys/dvb/camswclient.c | 162 | ||||
-rw-r--r-- | sys/dvb/camswclient.h | 53 | ||||
-rw-r--r-- | sys/dvb/camtransport.c | 505 | ||||
-rw-r--r-- | sys/dvb/camtransport.h | 115 | ||||
-rw-r--r-- | sys/dvb/camutils.c | 340 | ||||
-rw-r--r-- | sys/dvb/camutils.h | 59 | ||||
-rw-r--r-- | sys/dvb/dvbbasebin.c | 897 | ||||
-rw-r--r-- | sys/dvb/dvbbasebin.h | 75 | ||||
-rw-r--r-- | sys/dvb/gstdvb.c | 46 | ||||
-rw-r--r-- | sys/dvb/gstdvbsrc.c | 156 | ||||
-rw-r--r-- | sys/dvb/gstdvbsrc.h | 5 |
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 |