/src/vlc/modules/codec/scte18.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * scte18.c : SCTE-18 EAS decoder |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2016 - VideoLAN Authors |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify it |
7 | | * under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation; either version 2.1 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | | *****************************************************************************/ |
19 | | |
20 | | #ifdef HAVE_CONFIG_H |
21 | | # include "config.h" |
22 | | #endif |
23 | | |
24 | | #include <vlc_common.h> |
25 | | #include <vlc_plugin.h> |
26 | | #include <vlc_codec.h> |
27 | | |
28 | | #include "atsc_a65.h" |
29 | | #include "scte18.h" |
30 | | #include "substext.h" |
31 | | |
32 | | #include <time.h> |
33 | | |
34 | | /***************************************************************************** |
35 | | * Module descriptor. |
36 | | *****************************************************************************/ |
37 | | static int Open (vlc_object_t *); |
38 | | static void Close(vlc_object_t *); |
39 | | |
40 | 0 | vlc_module_begin () |
41 | 0 | set_description(N_("SCTE-18 decoder")) |
42 | 0 | set_shortname(N_("SCTE-18")) |
43 | 0 | set_capability( "spu decoder", 51) |
44 | 0 | set_subcategory(SUBCAT_INPUT_SCODEC) |
45 | 0 | set_callbacks(Open, Close) |
46 | 0 | vlc_module_end () |
47 | | |
48 | | typedef struct |
49 | | { |
50 | | atsc_a65_handle_t *p_handle; |
51 | | } decoder_sys_t; |
52 | | |
53 | | //#define GPS_UTC_EPOCH_OFFSET 315964800 |
54 | | //#define GPS_CUR_UTC_LEAP_OFFSET 16 /* 1 Jul 2015 */ |
55 | | |
56 | | typedef struct scte18_cea_t |
57 | | { |
58 | | uint16_t i_eas_event_id; |
59 | | char rgc_eas_originator_code[3]; |
60 | | char * psz_eas_event_code; |
61 | | char * psz_nature_of_activation; |
62 | | uint8_t alert_message_time_remaining; |
63 | | uint32_t event_start_time; |
64 | | uint16_t event_duration; |
65 | | uint8_t alert_priority; |
66 | | |
67 | | char * psz_alert_text; |
68 | | |
69 | | } scte18_cea_t; |
70 | | |
71 | | /**************************************************************************** |
72 | | * Local prototypes |
73 | | ****************************************************************************/ |
74 | 0 | #define BUF_ADVANCE(n) p_buffer += n; i_buffer -= n; |
75 | | |
76 | | static inline scte18_cea_t * scte18_cea_New(void) |
77 | 0 | { |
78 | 0 | return calloc( 1, sizeof(scte18_cea_t) ); |
79 | 0 | } |
80 | | |
81 | | static void scte18_cea_Free( scte18_cea_t *p_cea ) |
82 | 0 | { |
83 | 0 | free( p_cea->psz_alert_text ); |
84 | 0 | free( p_cea->psz_nature_of_activation ); |
85 | 0 | free( p_cea->psz_eas_event_code ); |
86 | 0 | free( p_cea ); |
87 | 0 | } |
88 | | |
89 | | static scte18_cea_t * scte18_cea_Decode( atsc_a65_handle_t *p_handle, const block_t *p_block ) |
90 | 0 | { |
91 | 0 | size_t len; |
92 | 0 | scte18_cea_t *p_cea = scte18_cea_New(); |
93 | 0 | if( !p_cea ) |
94 | 0 | return NULL; |
95 | | |
96 | 0 | const uint8_t *p_buffer = p_block->p_buffer; |
97 | 0 | size_t i_buffer = p_block->i_buffer; |
98 | |
|
99 | 0 | if( i_buffer < 34 || p_buffer[0] != 0 ) |
100 | 0 | goto error; |
101 | | |
102 | 0 | BUF_ADVANCE(1); |
103 | |
|
104 | 0 | p_cea->i_eas_event_id = GetWBE( p_buffer ); |
105 | 0 | BUF_ADVANCE(2); |
106 | |
|
107 | 0 | memcpy( p_cea->rgc_eas_originator_code, p_buffer, 3 ); |
108 | 0 | BUF_ADVANCE(3); |
109 | |
|
110 | 0 | len = p_buffer[0]; |
111 | 0 | if( i_buffer < 23 + len ) |
112 | 0 | goto error; |
113 | 0 | p_cea->psz_eas_event_code = malloc( len + 1 ); |
114 | 0 | memcpy( p_cea->psz_eas_event_code, &p_buffer[1], len ); |
115 | 0 | p_cea->psz_eas_event_code[len] = 0; |
116 | 0 | BUF_ADVANCE( len + 1 ); |
117 | |
|
118 | 0 | len = p_buffer[0]; |
119 | 0 | if( i_buffer < len + 22 ) |
120 | 0 | goto error; |
121 | 0 | p_cea->psz_nature_of_activation = atsc_a65_Decode_multiple_string( p_handle, &p_buffer[1], len ); |
122 | 0 | BUF_ADVANCE(1 + len); |
123 | |
|
124 | 0 | if( i_buffer < 21 ) |
125 | 0 | goto error; |
126 | 0 | p_cea->alert_message_time_remaining = p_buffer[0]; |
127 | 0 | BUF_ADVANCE(1); |
128 | |
|
129 | 0 | p_cea->event_start_time = GetDWBE( p_buffer ); |
130 | 0 | BUF_ADVANCE(4); |
131 | |
|
132 | 0 | p_cea->event_duration = GetWBE( p_buffer ); |
133 | 0 | if( p_cea->event_duration != 0 && ( p_cea->event_duration < 15 || p_cea->event_duration > 6000 ) ) |
134 | 0 | goto error; |
135 | 0 | BUF_ADVANCE(2); |
136 | |
|
137 | 0 | p_cea->alert_priority = p_buffer[1] & 0x0f; |
138 | 0 | switch( p_cea->alert_priority ) |
139 | 0 | { |
140 | 0 | case EAS_PRIORITY_TEST: |
141 | 0 | case EAS_PRIORITY_LOW: |
142 | 0 | case EAS_PRIORITY_MEDIUM: |
143 | 0 | case EAS_PRIORITY_HIGH: |
144 | 0 | case EAS_PRIORITY_MAX: |
145 | 0 | break; |
146 | 0 | default: |
147 | 0 | goto error; |
148 | 0 | } |
149 | | |
150 | 0 | BUF_ADVANCE(2); |
151 | |
|
152 | 0 | BUF_ADVANCE(2); //OOB_ID |
153 | |
|
154 | 0 | BUF_ADVANCE(2); // |
155 | 0 | BUF_ADVANCE(2); // |
156 | |
|
157 | 0 | BUF_ADVANCE(2); //audio_OOB_ID |
158 | |
|
159 | 0 | len = GetWBE( p_buffer ); |
160 | 0 | if( i_buffer < len + 2 ) |
161 | 0 | goto error; |
162 | 0 | p_cea->psz_alert_text = atsc_a65_Decode_multiple_string( p_handle, &p_buffer[2], len ); |
163 | |
|
164 | 0 | return p_cea; |
165 | | |
166 | 0 | error: |
167 | 0 | scte18_cea_Free( p_cea ); |
168 | 0 | return NULL; |
169 | 0 | } |
170 | | |
171 | | static int Decode( decoder_t *p_dec, block_t *p_block ) |
172 | 0 | { |
173 | 0 | if ( p_block == NULL ) /* No Drain */ |
174 | 0 | return VLCDEC_SUCCESS; |
175 | 0 | subpicture_t *p_spu = NULL; |
176 | |
|
177 | 0 | if (p_block->i_flags & (BLOCK_FLAG_CORRUPTED)) |
178 | 0 | goto exit; |
179 | | |
180 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
181 | |
|
182 | 0 | scte18_cea_t *p_cea = scte18_cea_Decode( p_sys->p_handle, p_block ); |
183 | 0 | if( p_cea ) |
184 | 0 | { |
185 | 0 | p_spu = decoder_NewSubpictureText( p_dec ); |
186 | 0 | if( p_spu ) |
187 | 0 | { |
188 | 0 | subtext_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; |
189 | |
|
190 | 0 | p_spu->i_start = p_block->i_pts; |
191 | 0 | if( p_cea->alert_message_time_remaining ) |
192 | 0 | p_spu->i_stop = p_spu->i_start + vlc_tick_from_sec( p_cea->alert_message_time_remaining ); |
193 | 0 | else |
194 | 0 | p_spu->i_stop = VLC_TICK_INVALID; |
195 | |
|
196 | 0 | p_spu->b_ephemer = true; |
197 | 0 | p_spu->b_absolute = false; |
198 | |
|
199 | 0 | p_spu_sys->region.inner_align = SUBPICTURE_ALIGN_TOP; |
200 | 0 | p_spu_sys->p_default_style->i_style_flags = STYLE_BOLD | STYLE_BACKGROUND; |
201 | 0 | p_spu_sys->p_default_style->i_features |= STYLE_HAS_FLAGS; |
202 | 0 | p_spu_sys->p_default_style->i_background_color = 0x000000; |
203 | 0 | p_spu_sys->p_default_style->i_background_alpha = STYLE_ALPHA_OPAQUE; |
204 | 0 | p_spu_sys->p_default_style->i_features |= STYLE_HAS_BACKGROUND_COLOR | STYLE_HAS_BACKGROUND_ALPHA; |
205 | 0 | p_spu_sys->p_default_style->i_font_color = 0xFF0000; |
206 | 0 | p_spu_sys->p_default_style->i_features |= STYLE_HAS_FONT_COLOR; |
207 | |
|
208 | 0 | p_spu_sys->region.p_segments = text_segment_New( p_cea->psz_alert_text ); |
209 | 0 | decoder_QueueSub( p_dec, p_spu ); |
210 | 0 | } |
211 | 0 | msg_Info( p_dec, "Received %s", p_cea->psz_alert_text ); |
212 | 0 | scte18_cea_Free( p_cea ); |
213 | 0 | } |
214 | |
|
215 | 0 | exit: |
216 | 0 | block_Release( p_block ); |
217 | 0 | return VLCDEC_SUCCESS; |
218 | 0 | } |
219 | | |
220 | | static int Open( vlc_object_t *object ) |
221 | 0 | { |
222 | 0 | decoder_t *dec = (decoder_t *)object; |
223 | |
|
224 | 0 | if ( dec->fmt_in->i_codec != VLC_CODEC_SCTE_18 ) |
225 | 0 | return VLC_EGENERIC; |
226 | | |
227 | 0 | decoder_sys_t *p_sys = malloc( sizeof(decoder_sys_t) ); |
228 | 0 | if( unlikely(!p_sys) ) |
229 | 0 | return VLC_ENOMEM; |
230 | | |
231 | 0 | p_sys->p_handle = atsc_a65_handle_New( NULL ); |
232 | 0 | if( !p_sys->p_handle ) |
233 | 0 | { |
234 | 0 | free( p_sys ); |
235 | 0 | return VLC_EGENERIC; |
236 | 0 | } |
237 | | |
238 | 0 | dec->p_sys = p_sys; |
239 | 0 | dec->pf_decode = Decode; |
240 | 0 | dec->fmt_out.i_codec = 0; |
241 | |
|
242 | 0 | return VLC_SUCCESS; |
243 | 0 | } |
244 | | |
245 | | static void Close( vlc_object_t *p_object ) |
246 | 0 | { |
247 | 0 | decoder_t *p_dec = (decoder_t *)p_object; |
248 | 0 | decoder_sys_t *p_sys = (decoder_sys_t *) p_dec->p_sys; |
249 | 0 | atsc_a65_handle_Release( p_sys->p_handle ); |
250 | 0 | free( p_sys ); |
251 | 0 | } |
252 | | |