/*****************************************************************************
* 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 .
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include
#include
#include
#include "atsc_a65.h"
#include "scte18.h"
#include "substext.h"
#include
/*****************************************************************************
* 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( "spu 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 int Decode( decoder_t *p_dec, block_t *p_block )
{
if ( p_block == NULL ) /* No Drain */
return VLCDEC_SUCCESS;
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_TICK_INVALID;
p_spu->b_ephemer = true;
p_spu->b_absolute = false;
p_spu_sys->region.inner_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->region.p_segments = text_segment_New( p_cea->psz_alert_text );
decoder_QueueSub( p_dec, p_spu );
}
msg_Info( p_dec, "Received %s", p_cea->psz_alert_text );
scte18_cea_Free( p_cea );
}
exit:
block_Release( p_block );
return VLCDEC_SUCCESS;
}
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 = Decode;
dec->fmt_out.i_codec = 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 );
}