You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
7.2 KiB
251 lines
7.2 KiB
/*****************************************************************************
|
|
* 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( "spu decoder", 51)
|
|
set_subcategory(SUBCAT_INPUT_SCODEC)
|
|
set_callbacks(Open, Close)
|
|
vlc_module_end ()
|
|
|
|
typedef struct
|
|
{
|
|
atsc_a65_handle_t *p_handle;
|
|
} decoder_sys_t;
|
|
|
|
//#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(void)
|
|
{
|
|
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;
|
|
|
|
decoder_sys_t *p_sys = p_dec->p_sys;
|
|
|
|
scte18_cea_t *p_cea = scte18_cea_Decode( p_sys->p_handle, p_block );
|
|
if( p_cea )
|
|
{
|
|
p_spu = decoder_NewSubpictureText( p_dec );
|
|
if( p_spu )
|
|
{
|
|
subtext_updater_sys_t *p_spu_sys = p_spu->updater.sys;
|
|
|
|
p_spu->i_start = p_block->i_pts;
|
|
if( p_cea->alert_message_time_remaining )
|
|
p_spu->i_stop = p_spu->i_start + vlc_tick_from_sec( p_cea->alert_message_time_remaining );
|
|
else
|
|
p_spu->i_stop = VLC_TICK_INVALID;
|
|
|
|
p_spu->b_ephemer = true;
|
|
|
|
p_spu_sys->region.b_absolute = false; p_spu_sys->region.b_in_window = 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 );
|
|
}
|
|
|