From 36f547eced7fd01877b3b019e5d6a74e9a8939b3 Mon Sep 17 00:00:00 2001
From: Francois Cartegnie <fcvlcdev@free.fr>
Date: Fri, 22 Jan 2016 22:39:11 +0100
Subject: [PATCH] codecs: add SCTE-18 decoder

---
 include/vlc_fourcc.h      |   1 +
 modules/MODULES_LIST      |   1 +
 modules/codec/Makefile.am |   4 +
 modules/codec/scte18.c    | 251 ++++++++++++++++++++++++++++++++++++++
 modules/codec/scte18.h    |  59 +++++++++
 po/POTFILES.in            |   2 +
 6 files changed, 318 insertions(+)
 create mode 100644 modules/codec/scte18.h

diff --git a/include/vlc_fourcc.h b/include/vlc_fourcc.h
index 7b518be147ca..1b971dbc1895 100644
--- a/include/vlc_fourcc.h
+++ b/include/vlc_fourcc.h
@@ -500,6 +500,7 @@
 #define VLC_CODEC_BD_PG     VLC_FOURCC('b','d','p','g')
 /* EBU STL (TECH. 3264-E) */
 #define VLC_CODEC_EBU_STL   VLC_FOURCC('S','T','L',' ')
+#define VLC_CODEC_SCTE_18   VLC_FOURCC('S','C','1','8')
 #define VLC_CODEC_SCTE_27   VLC_FOURCC('S','C','2','7')
 /* EIA-608 */
 #define VLC_CODEC_EIA608_1  VLC_FOURCC('c','c','1',' ')
diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
index 442fc8a775db..0d57436c0a25 100644
--- a/modules/MODULES_LIST
+++ b/modules/MODULES_LIST
@@ -330,6 +330,7 @@ $Id$
  * scene: scene video filter
  * schroedinger: Schroedinger video decoder
  * screen: a input module that takes screenshots of the primary monitor
+ * scte18: SCTE-18/Emergency Alert Messaging for Cable decoder
  * scte27: SCTE-27/Digicipher subtitles decoder
  * sd_journal: logger output to SystemD journal
  * sdl_image: SDL-based image decoder
diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
index 4a134a45fd00..a56b1f566a3d 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -182,6 +182,10 @@ libaribsub_plugin_la_LIBADD = $(ARIBB24_LIBS)
 EXTRA_LTLIBRARIES += libaribsub_plugin.la
 codec_LTLIBRARIES += $(LTLIBaribsub)
 
+libscte18_plugin_la_SOURCES = codec/scte18.c codec/scte18.h \
+                              codec/atsc_a65.c codec/atsc_a65.h
+codec_LTLIBRARIES += libscte18_plugin.la
+
 libscte27_plugin_la_SOURCES = codec/scte27.c
 codec_LTLIBRARIES += libscte27_plugin.la
 
diff --git a/modules/codec/scte18.c b/modules/codec/scte18.c
index e69de29bb2d1..884639f8af61 100644
--- a/modules/codec/scte18.c
+++ b/modules/codec/scte18.c
@@ -0,0 +1,251 @@
+/*****************************************************************************
+ * scte18.c : SCTE-18 EAS decoder
+ *****************************************************************************
+ * Copyright (C) 2016 - VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_codec.h>
+
+#include "atsc_a65.h"
+#include "scte18.h"
+#include "substext.h"
+
+#include <time.h>
+
+/*****************************************************************************
+ * Module descriptor.
+ *****************************************************************************/
+static int  Open (vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin ()
+    set_description(N_("SCTE-18 decoder"))
+    set_shortname(N_("SCTE-18"))
+    set_capability( "decoder", 51)
+    set_category(CAT_INPUT)
+    set_subcategory(SUBCAT_INPUT_SCODEC)
+    set_callbacks(Open, Close)
+vlc_module_end ()
+
+struct decoder_sys_t
+{
+    atsc_a65_handle_t *p_handle;
+};
+
+//#define GPS_UTC_EPOCH_OFFSET 315964800
+//#define GPS_CUR_UTC_LEAP_OFFSET  16 /* 1 Jul 2015 */
+
+typedef struct scte18_cea_t
+{
+    uint16_t i_eas_event_id;
+    char     rgc_eas_originator_code[3];
+    char *   psz_eas_event_code;
+    char *   psz_nature_of_activation;
+    uint8_t  alert_message_time_remaining;
+    uint32_t event_start_time;
+    uint16_t event_duration;
+    uint8_t  alert_priority;
+
+    char *   psz_alert_text;
+
+} scte18_cea_t;
+
+/****************************************************************************
+ * Local prototypes
+ ****************************************************************************/
+#define BUF_ADVANCE(n) p_buffer += n; i_buffer -= n;
+
+static inline scte18_cea_t * scte18_cea_New()
+{
+    return calloc( 1, sizeof(scte18_cea_t) );
+}
+
+static void scte18_cea_Free( scte18_cea_t *p_cea )
+{
+    free( p_cea->psz_alert_text );
+    free( p_cea->psz_nature_of_activation );
+    free( p_cea->psz_eas_event_code );
+    free( p_cea );
+}
+
+static scte18_cea_t * scte18_cea_Decode( atsc_a65_handle_t *p_handle, const block_t *p_block )
+{
+    size_t len;
+    scte18_cea_t *p_cea = scte18_cea_New();
+    if( !p_cea )
+        return NULL;
+
+    const uint8_t *p_buffer = p_block->p_buffer;
+    size_t i_buffer = p_block->i_buffer;
+
+    if( i_buffer < 34 || p_buffer[0] != 0 )
+        goto error;
+
+    BUF_ADVANCE(1);
+
+    p_cea->i_eas_event_id = GetWBE( p_buffer );
+    BUF_ADVANCE(2);
+
+    memcpy( p_cea->rgc_eas_originator_code, p_buffer, 3 );
+    BUF_ADVANCE(3);
+
+    len = p_buffer[0];
+    if( i_buffer < 23 + len )
+        goto error;
+    p_cea->psz_eas_event_code = malloc( len + 1 );
+    memcpy( p_cea->psz_eas_event_code, &p_buffer[1], len );
+    p_cea->psz_eas_event_code[len] = 0;
+    BUF_ADVANCE( len + 1 );
+
+    len = p_buffer[0];
+    if( i_buffer < len + 22 )
+        goto error;
+    p_cea->psz_nature_of_activation = atsc_a65_Decode_multiple_string( p_handle, &p_buffer[1], len );
+    BUF_ADVANCE(1 + len);
+
+    if( i_buffer < 21 )
+        goto error;
+    p_cea->alert_message_time_remaining = p_buffer[0];
+    BUF_ADVANCE(1);
+
+    p_cea->event_start_time = GetDWBE( p_buffer );
+    BUF_ADVANCE(4);
+
+    p_cea->event_duration = GetWBE( p_buffer );
+    if( p_cea->event_duration != 0 && ( p_cea->event_duration < 15 || p_cea->event_duration > 6000 ) )
+        goto error;
+    BUF_ADVANCE(2);
+
+    p_cea->alert_priority = p_buffer[1] & 0x0f;
+    switch( p_cea->alert_priority )
+    {
+        case EAS_PRIORITY_TEST:
+        case EAS_PRIORITY_LOW:
+        case EAS_PRIORITY_MEDIUM:
+        case EAS_PRIORITY_HIGH:
+        case EAS_PRIORITY_MAX:
+            break;
+        default:
+            goto error;
+    }
+
+    BUF_ADVANCE(2);
+
+    BUF_ADVANCE(2); //OOB_ID
+
+    BUF_ADVANCE(2); //
+    BUF_ADVANCE(2); //
+
+    BUF_ADVANCE(2); //audio_OOB_ID
+
+    len = GetWBE( p_buffer );
+    if( i_buffer < len + 2 )
+        goto error;
+    p_cea->psz_alert_text = atsc_a65_Decode_multiple_string( p_handle, &p_buffer[2], len );
+
+    return p_cea;
+
+error:
+    scte18_cea_Free( p_cea );
+    return NULL;
+}
+
+static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
+{
+    if ( pp_block == NULL || *pp_block == NULL )
+        return NULL;
+    block_t *p_block = *pp_block; *pp_block = NULL;
+    subpicture_t  *p_spu = NULL;
+
+    if (p_block->i_flags & (BLOCK_FLAG_CORRUPTED))
+        goto exit;
+
+    scte18_cea_t *p_cea = scte18_cea_Decode( p_dec->p_sys->p_handle, p_block );
+    if( p_cea )
+    {
+        p_spu = decoder_NewSubpictureText( p_dec );
+        if( p_spu )
+        {
+            subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
+
+            p_spu->i_start = p_block->i_pts;
+            if( p_cea->alert_message_time_remaining )
+                p_spu->i_stop = p_spu->i_start + CLOCK_FREQ * p_cea->alert_message_time_remaining;
+            else
+                p_spu->i_stop = VLC_TS_INVALID;
+
+            p_spu->b_ephemer  = true;
+            p_spu->b_absolute = false;
+
+            p_spu_sys->align = SUBPICTURE_ALIGN_TOP;
+            p_spu_sys->p_default_style->i_style_flags = STYLE_BOLD | STYLE_BACKGROUND;
+            p_spu_sys->p_default_style->i_features |= STYLE_HAS_FLAGS;
+            p_spu_sys->p_default_style->i_background_color = 0x000000;
+            p_spu_sys->p_default_style->i_background_alpha = STYLE_ALPHA_OPAQUE;
+            p_spu_sys->p_default_style->i_features |= STYLE_HAS_BACKGROUND_COLOR | STYLE_HAS_BACKGROUND_ALPHA;
+            p_spu_sys->p_default_style->i_font_color = 0xFF0000;
+            p_spu_sys->p_default_style->i_features |= STYLE_HAS_FONT_COLOR;
+
+            p_spu_sys->p_segments = text_segment_New( p_cea->psz_alert_text );
+        }
+        msg_Info( p_dec, "Received %s", p_cea->psz_alert_text );
+        scte18_cea_Free( p_cea );
+    }
+
+exit:
+    block_Release( p_block );
+    return p_spu;
+}
+
+static int Open( vlc_object_t *object )
+{
+    decoder_t *dec = (decoder_t *)object;
+
+    if ( dec->fmt_in.i_codec != VLC_CODEC_SCTE_18 )
+        return VLC_EGENERIC;
+
+    decoder_sys_t *p_sys = malloc( sizeof(decoder_sys_t) );
+    if( unlikely(!p_sys) )
+        return VLC_ENOMEM;
+
+    p_sys->p_handle = atsc_a65_handle_New( NULL );
+    if( !p_sys->p_handle )
+    {
+        free( p_sys );
+        return VLC_EGENERIC;
+    }
+
+    dec->p_sys = p_sys;
+    dec->pf_decode_sub = Decode;
+    es_format_Init( &dec->fmt_out, SPU_ES, 0 );
+
+    return VLC_SUCCESS;
+}
+
+static void Close( vlc_object_t *p_object )
+{
+    decoder_t *p_dec = (decoder_t *)p_object;
+    decoder_sys_t *p_sys = (decoder_sys_t *) p_dec->p_sys;
+    atsc_a65_handle_Release( p_sys->p_handle );
+    free( p_sys );
+}
+
diff --git a/modules/codec/scte18.h b/modules/codec/scte18.h
new file mode 100644
index 000000000000..e0f46750d26a
--- /dev/null
+++ b/modules/codec/scte18.h
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ * scte18.h : SCTE-18 EAS decoder
+ *****************************************************************************
+ * Copyright (C) 2016 - VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifndef VLC_SCTE18_H
+#define VLC_SCTE18_H
+
+#define SCTE18_SI_BASE_PID 0x1FFB
+#define SCTE18_TABLE_ID    0xD8
+#define SCTE18_DESCRIPTION N_("Emergency Alert Messaging for Cable")
+
+enum
+{
+    EAS_PRIORITY_TEST = 0,
+    EAS_PRIORITY_LOW = 3,
+    EAS_PRIORITY_MEDIUM = 7,
+    EAS_PRIORITY_HIGH = 11,
+    EAS_PRIORITY_MAX = 15
+};
+
+/* Get priority without decoding
+ */
+static inline int scte18_get_EAS_priority( const uint8_t *p_buffer, size_t i_buffer )
+{
+    if( i_buffer < 17 || p_buffer[0] )
+        return -1;
+
+    size_t i_offset = 6;
+    size_t i_len = p_buffer[i_offset]; /* EAS code Len */
+    i_offset += i_len + 1; /* EAS code Text */
+    if( i_offset >= i_buffer )
+        return -1;
+
+    i_len = p_buffer[i_offset]; /* NOA Len */
+    i_offset += i_len + 1; /* NOA Text */
+
+    i_offset += 1 + 4 + 2 + 1;
+
+    if( i_offset >= i_buffer )
+        return -1;
+
+    return (p_buffer[i_offset] & 0x0f);
+}
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index db4e99ca6c5c..cdaf3dad82eb 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -394,6 +394,8 @@ modules/codec/quicktime.c
 modules/codec/rawvideo.c
 modules/codec/rtpvideo.c
 modules/codec/schroedinger.c
+modules/codec/scte18.c
+modules/codec/scte18.h
 modules/codec/sdl_image.c
 modules/codec/shine.c
 modules/codec/speex.c
-- 
GitLab