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.
This commit is contained in:
Alessandro Decina 2007-10-16 17:38:05 +00:00 committed by Zaheer Abbas Merali
parent 389904f624
commit c205b740d4
27 changed files with 4520 additions and 74 deletions

View file

@ -1,3 +1,40 @@
2007-10-16 Zaheer Abbas Merali <zaheerabbas at merali dot org>
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.
2007-10-16 Zaheer Abbas Merali <zaheerabbas at merali dot org>
patch by: Alessandro Decina

View file

@ -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
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
noinst_HEADERS = gstdvbsrc.h

33
sys/dvb/cam.c Normal file
View file

@ -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");
}

33
sys/dvb/cam.h Normal file
View file

@ -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 */

292
sys/dvb/camapplication.c Normal file
View file

@ -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);
}

84
sys/dvb/camapplication.h Normal file
View file

@ -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 */

View file

@ -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;
}

View file

@ -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 */

View file

@ -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;
}

View file

@ -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 */

213
sys/dvb/camdevice.c Normal file
View file

@ -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);
}

69
sys/dvb/camdevice.h Normal file
View file

@ -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 */

View file

@ -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;
}

View file

@ -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 */

595
sys/dvb/camsession.c Normal file
View file

@ -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;
}

96
sys/dvb/camsession.h Normal file
View file

@ -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 */

162
sys/dvb/camswclient.c Normal file
View file

@ -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 */ );
}

53
sys/dvb/camswclient.h Normal file
View file

@ -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 */

505
sys/dvb/camtransport.c Normal file
View file

@ -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);
}

115
sys/dvb/camtransport.h Normal file
View file

@ -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 */

340
sys/dvb/camutils.c Normal file
View file

@ -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;
}

59
sys/dvb/camutils.h Normal file
View file

@ -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 */

897
sys/dvb/dvbbasebin.c Normal file
View file

@ -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);
}

75
sys/dvb/dvbbasebin.h Normal file
View file

@ -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 */

46
sys/dvb/gstdvb.c Normal file
View file

@ -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)

View file

@ -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);
}

View file

@ -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