diff --git a/include/vlc_fourcc.h b/include/vlc_fourcc.h index 7b518be147cafe28bf3ca0456bdc6f1508b5b6f4..1b971dbc189511d08b77010b4e64d45b32fc4c7b 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 442fc8a775db2640e21698aa9647b631baa4b7bb..0d57436c0a25e687cf3f8a5d7590aceb8f8620fe 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 4a134a45fd0034cf6260d3c7ff229942240600ea..a56b1f566a3d0b5250a7acab59e68b46d994b89e 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..884639f8af61494eeee46f251e46109fdf182741 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 0000000000000000000000000000000000000000..e0f46750d26ad60ee40f78c3f707dbd469dcb291 --- /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 db4e99ca6c5c72df51d9cf0403ab862c37eb7b39..cdaf3dad82eb03df72ff5ebfc650df009f4990d8 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