Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
Steve Lhomme
VLC
Commits
6ce3ecfc
Commit
6ce3ecfc
authored
Dec 06, 2012
by
Laurent Aimar
Browse files
Added SCTE-27 decoder.
parent
d2b19589
Changes
2
Hide whitespace changes
Inline
Side-by-side
modules/codec/Modules.am
View file @
6ce3ecfc
...
...
@@ -56,6 +56,7 @@ SOURCES_dmo = dmo/dmo.c dmo/dmo.h dmo/buffer.c
SOURCES_ddummy = ddummy.c
SOURCES_edummy = edummy.c
SOURCES_fdkaac = fdkaac.c
SOURCES_scte27 = scte27.c
libvlc_LTLIBRARIES += \
liba52_plugin.la \
...
...
@@ -77,6 +78,7 @@ libvlc_LTLIBRARIES += \
libsubsdec_plugin.la \
libsubsusf_plugin.la \
libstl_plugin.la \
libscte27_plugin.la \
$(NULL)
if ENABLE_SOUT
...
...
modules/codec/scte27.c
0 → 100644
View file @
6ce3ecfc
/*****************************************************************************
* scte27.c : SCTE-27 subtitles decoder
*****************************************************************************
* Copyright (C) Laurent Aimar
* $Id$
*
* Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* 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 Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include
<vlc_common.h>
#include
<vlc_plugin.h>
#include
<vlc_codec.h>
#include
<vlc_bits.h>
#include
<assert.h>
/*****************************************************************************
* Module descriptor.
*****************************************************************************/
static
int
Open
(
vlc_object_t
*
);
static
void
Close
(
vlc_object_t
*
);
vlc_module_begin
()
set_description
(
N_
(
"SCTE-27 decoder"
))
set_shortname
(
N_
(
"SCTE-27"
))
set_capability
(
"decoder"
,
51
)
set_category
(
CAT_INPUT
)
set_subcategory
(
SUBCAT_INPUT_SCODEC
)
set_callbacks
(
Open
,
Close
)
vlc_module_end
()
/****************************************************************************
* Local prototypes
****************************************************************************/
struct
decoder_sys_t
{
int
segment_id
;
int
segment_size
;
uint8_t
*
segment_buffer
;
mtime_t
segment_date
;
};
typedef
struct
{
uint8_t
y
,
u
,
v
;
uint8_t
alpha
;
}
scte27_color_t
;
static
const
scte27_color_t
scte27_color_transparent
=
{
.
y
=
0x00
,
.
u
=
0x80
,
.
v
=
0x80
,
.
alpha
=
0x00
,
};
static
scte27_color_t
bs_read_color
(
bs_t
*
bs
)
{
scte27_color_t
color
;
/* XXX it's unclear if a value of 0 in Y/U/V means a transparent pixel */
color
.
y
=
bs_read
(
bs
,
5
)
<<
3
;
color
.
alpha
=
bs_read1
(
bs
)
?
0xff
:
0x80
;
color
.
u
=
bs_read
(
bs
,
5
)
<<
3
;
color
.
v
=
bs_read
(
bs
,
5
)
<<
3
;
return
color
;
}
static
inline
void
SetPixel
(
picture_t
*
picture
,
int
x
,
int
y
,
int
value
)
{
picture
->
p
->
p_pixels
[
y
*
picture
->
p
->
i_pitch
+
x
]
=
value
;
}
static
subpicture_region_t
*
DecodeSimpleBitmap
(
decoder_t
*
dec
,
const
uint8_t
*
data
,
int
size
)
{
VLC_UNUSED
(
dec
);
/* Parse the bitmap and its properties */
bs_t
bs
;
bs_init
(
&
bs
,
data
,
size
);
bs_skip
(
&
bs
,
5
);
int
is_framed
=
bs_read
(
&
bs
,
1
);
int
outline_style
=
bs_read
(
&
bs
,
2
);
scte27_color_t
character_color
=
bs_read_color
(
&
bs
);
int
top_h
=
bs_read
(
&
bs
,
12
);
int
top_v
=
bs_read
(
&
bs
,
12
);
int
bottom_h
=
bs_read
(
&
bs
,
12
);
int
bottom_v
=
bs_read
(
&
bs
,
12
);
if
(
top_h
>=
bottom_h
||
top_v
>=
bottom_v
)
return
NULL
;
int
frame_top_h
=
top_h
;
int
frame_top_v
=
top_v
;
int
frame_bottom_h
=
bottom_h
;
int
frame_bottom_v
=
bottom_v
;
scte27_color_t
frame_color
=
scte27_color_transparent
;
if
(
is_framed
)
{
frame_top_h
=
bs_read
(
&
bs
,
12
);
frame_top_v
=
bs_read
(
&
bs
,
12
);
frame_bottom_h
=
bs_read
(
&
bs
,
12
);
frame_bottom_v
=
bs_read
(
&
bs
,
12
);
frame_color
=
bs_read_color
(
&
bs
);
if
(
frame_top_h
>
top_h
||
frame_top_v
>
top_v
||
frame_bottom_h
<
bottom_h
||
frame_bottom_v
<
bottom_v
)
return
NULL
;
}
int
outline_thickness
=
0
;
scte27_color_t
outline_color
=
scte27_color_transparent
;
int
shadow_right
=
0
;
int
shadow_bottom
=
0
;
scte27_color_t
shadow_color
=
scte27_color_transparent
;
if
(
outline_style
==
1
)
{
bs_skip
(
&
bs
,
4
);
outline_thickness
=
bs_read
(
&
bs
,
4
);
outline_color
=
bs_read_color
(
&
bs
);
}
else
if
(
outline_style
==
2
)
{
shadow_right
=
bs_read
(
&
bs
,
4
);
shadow_bottom
=
bs_read
(
&
bs
,
4
);
shadow_color
=
bs_read_color
(
&
bs
);
}
else
if
(
outline_style
==
3
)
{
bs_skip
(
&
bs
,
24
);
}
bs_skip
(
&
bs
,
16
);
// bitmap_compressed_length
int
bitmap_h
=
bottom_h
-
top_h
;
int
bitmap_v
=
bottom_v
-
top_v
;
int
bitmap_size
=
bitmap_h
*
bitmap_v
;
bool
*
bitmap
=
malloc
(
bitmap_size
*
sizeof
(
*
bitmap
));
if
(
!
bitmap
)
return
NULL
;
for
(
int
position
=
0
;
position
<
bitmap_size
;)
{
if
(
bs_eof
(
&
bs
))
{
for
(;
position
<
bitmap_size
;
position
++
)
bitmap
[
position
]
=
false
;
break
;
}
int
run_on_length
=
0
;
int
run_off_length
=
0
;
if
(
!
bs_read1
(
&
bs
))
{
if
(
!
bs_read1
(
&
bs
))
{
if
(
!
bs_read1
(
&
bs
))
{
if
(
bs_read
(
&
bs
,
2
)
==
1
)
{
int
next
=
__MIN
((
position
/
bitmap_h
+
1
)
*
bitmap_h
,
bitmap_size
);
for
(;
position
<
next
;
position
++
)
bitmap
[
position
]
=
false
;
}
}
else
{
run_on_length
=
4
;
}
}
else
{
run_off_length
=
6
;
}
}
else
{
run_on_length
=
3
;
run_off_length
=
5
;
}
if
(
run_on_length
>
0
)
{
int
run
=
bs_read
(
&
bs
,
run_on_length
);
if
(
!
run
)
run
=
1
<<
run_on_length
;
for
(;
position
<
bitmap_size
&&
run
>
0
;
position
++
,
run
--
)
bitmap
[
position
]
=
true
;
}
if
(
run_off_length
>
0
)
{
int
run
=
bs_read
(
&
bs
,
run_off_length
);
if
(
!
run
)
run
=
1
<<
run_off_length
;
for
(;
position
<
bitmap_size
&&
run
>
0
;
position
++
,
run
--
)
bitmap
[
position
]
=
false
;
}
}
/* Render the bitmap into a subpicture_region_t */
/* Reserve the place for the style
* FIXME It's unclear if it is needed or if the bitmap should already include
* the needed margin (I think the samples I have do both). */
int
margin_h
=
0
;
int
margin_v
=
0
;
if
(
outline_style
==
1
)
{
margin_h
=
margin_v
=
outline_thickness
;
}
else
if
(
outline_style
==
2
)
{
margin_h
=
shadow_right
;
margin_v
=
shadow_bottom
;
}
frame_top_h
-=
margin_h
;
frame_top_v
-=
margin_v
;
frame_bottom_h
+=
margin_h
;
frame_bottom_v
+=
margin_v
;
const
int
frame_h
=
frame_bottom_h
-
frame_top_h
;
const
int
frame_v
=
frame_bottom_v
-
frame_top_v
;
const
int
bitmap_oh
=
top_h
-
frame_top_h
;
const
int
bitmap_ov
=
top_v
-
frame_top_v
;
enum
{
COLOR_FRAME
,
COLOR_CHARACTER
,
COLOR_OUTLINE
,
COLOR_SHADOW
,
};
video_palette_t
palette
=
{
.
i_entries
=
4
,
.
palette
=
{
[
COLOR_FRAME
]
=
{
frame_color
.
y
,
frame_color
.
u
,
frame_color
.
v
,
frame_color
.
alpha
},
[
COLOR_CHARACTER
]
=
{
character_color
.
y
,
character_color
.
u
,
character_color
.
v
,
character_color
.
alpha
},
[
COLOR_OUTLINE
]
=
{
outline_color
.
y
,
outline_color
.
u
,
outline_color
.
v
,
outline_color
.
alpha
},
[
COLOR_SHADOW
]
=
{
shadow_color
.
y
,
shadow_color
.
u
,
shadow_color
.
v
,
shadow_color
.
alpha
},
},
};
video_format_t
fmt
=
{
.
i_chroma
=
VLC_CODEC_YUVP
,
.
i_width
=
frame_h
,
.
i_height
=
frame_v
,
.
i_sar_num
=
0
,
/* Use video AR */
.
i_sar_den
=
1
,
.
p_palette
=
&
palette
,
};
subpicture_region_t
*
r
=
subpicture_region_New
(
&
fmt
);
if
(
!
r
)
{
free
(
bitmap
);
return
NULL
;
}
r
->
i_x
=
frame_top_h
;
r
->
i_y
=
frame_top_v
;
/* Fill up with frame (background) color */
for
(
int
y
=
0
;
y
<
frame_v
;
y
++
)
memset
(
&
r
->
p_picture
->
p
->
p_pixels
[
y
*
r
->
p_picture
->
p
->
i_pitch
],
COLOR_FRAME
,
frame_h
);
/* Draw the outline/shadow if requested */
if
(
outline_style
==
1
)
{
/* Draw an outline
* XXX simple but slow and of low quality (no anti-aliasing) */
bool
circle
[
16
][
16
];
for
(
int
dy
=
0
;
dy
<=
15
;
dy
++
)
{
for
(
int
dx
=
0
;
dx
<=
15
;
dx
++
)
circle
[
dy
][
dx
]
=
(
dx
>
0
||
dy
>
0
)
&&
dx
*
dx
+
dy
*
dy
<=
outline_thickness
*
outline_thickness
;
}
for
(
int
by
=
0
;
by
<
bitmap_v
;
by
++
)
{
for
(
int
bx
=
0
;
bx
<
bitmap_h
;
bx
++
)
{
if
(
!
bitmap
[
by
*
bitmap_h
+
bx
])
continue
;
for
(
int
dy
=
0
;
dy
<=
outline_thickness
;
dy
++
)
{
for
(
int
dx
=
0
;
dx
<=
outline_thickness
;
dx
++
)
{
if
(
circle
[
dy
][
dx
])
{
SetPixel
(
r
->
p_picture
,
bx
+
bitmap_oh
+
dx
,
by
+
bitmap_ov
+
dy
,
COLOR_OUTLINE
);
SetPixel
(
r
->
p_picture
,
bx
+
bitmap_oh
-
dx
,
by
+
bitmap_ov
+
dy
,
COLOR_OUTLINE
);
SetPixel
(
r
->
p_picture
,
bx
+
bitmap_oh
+
dx
,
by
+
bitmap_ov
-
dy
,
COLOR_OUTLINE
);
SetPixel
(
r
->
p_picture
,
bx
+
bitmap_oh
-
dx
,
by
+
bitmap_ov
-
dy
,
COLOR_OUTLINE
);
}
}
}
}
}
}
else
if
(
outline_style
==
2
)
{
/* Draw a shadow by drawing the character shifted by shaddow right/bottom */
for
(
int
by
=
0
;
by
<
bitmap_v
;
by
++
)
{
for
(
int
bx
=
0
;
bx
<
bitmap_h
;
bx
++
)
{
if
(
bitmap
[
by
*
bitmap_h
+
bx
])
SetPixel
(
r
->
p_picture
,
bx
+
bitmap_oh
+
shadow_right
,
by
+
bitmap_ov
+
shadow_bottom
,
COLOR_SHADOW
);
}
}
}
/* Draw the character */
for
(
int
by
=
0
;
by
<
bitmap_v
;
by
++
)
{
for
(
int
bx
=
0
;
bx
<
bitmap_h
;
bx
++
)
{
if
(
bitmap
[
by
*
bitmap_h
+
bx
])
SetPixel
(
r
->
p_picture
,
bx
+
bitmap_oh
,
by
+
bitmap_ov
,
COLOR_CHARACTER
);
}
}
free
(
bitmap
);
return
r
;
}
static
subpicture_t
*
DecodeSubtitleMessage
(
decoder_t
*
dec
,
const
uint8_t
*
data
,
int
size
,
mtime_t
date
)
{
if
(
size
<
12
)
goto
error
;
/* Parse the header */
bool
pre_clear_display
=
data
[
3
]
&
0x80
;
int
display_standard
=
data
[
3
]
&
0x1f
;
int
subtitle_type
=
data
[
8
]
>>
4
;
int
display_duration
=
((
data
[
8
]
&
0x07
)
<<
8
)
|
data
[
9
];
int
block_length
=
GetWBE
(
&
data
[
10
]);
size
-=
12
;
data
+=
12
;
if
(
block_length
>
size
)
goto
error
;
if
(
subtitle_type
==
1
)
{
subpicture_region_t
*
region
=
DecodeSimpleBitmap
(
dec
,
data
,
block_length
);
if
(
!
region
)
goto
error
;
subpicture_t
*
sub
=
decoder_NewSubpicture
(
dec
,
NULL
);
if
(
!
sub
)
{
subpicture_region_Delete
(
region
);
return
NULL
;
}
int
frame_duration
;
switch
(
display_standard
)
{
case
0
:
sub
->
i_original_picture_width
=
720
;
sub
->
i_original_picture_height
=
480
;
frame_duration
=
33367
;
break
;
case
1
:
sub
->
i_original_picture_width
=
720
;
sub
->
i_original_picture_height
=
576
;
frame_duration
=
40000
;
break
;
case
2
:
sub
->
i_original_picture_width
=
1280
;
sub
->
i_original_picture_height
=
720
;
frame_duration
=
16683
;
break
;
case
3
:
sub
->
i_original_picture_width
=
1920
;
sub
->
i_original_picture_height
=
1080
;
frame_duration
=
16683
;
break
;
default:
msg_Warn
(
dec
,
"Unknown display standard"
);
sub
->
i_original_picture_width
=
0
;
sub
->
i_original_picture_height
=
0
;
frame_duration
=
40000
;
break
;
}
sub
->
b_absolute
=
true
;
if
(
!
pre_clear_display
)
msg_Warn
(
dec
,
"SCTE-27 subtitles without pre_clear_display flag are not well supported"
);
sub
->
b_ephemer
=
true
;
sub
->
i_start
=
date
;
sub
->
i_stop
=
date
+
display_duration
*
frame_duration
;
sub
->
p_region
=
region
;
return
sub
;
}
else
{
/* Reserved */
return
NULL
;
}
error:
msg_Err
(
dec
,
"corrupted subtitle_message"
);
return
NULL
;
}
static
subpicture_t
*
Decode
(
decoder_t
*
dec
,
block_t
**
block
)
{
decoder_sys_t
*
sys
=
dec
->
p_sys
;
if
(
block
==
NULL
||
*
block
==
NULL
)
return
NULL
;
block_t
*
b
=
*
block
;
*
block
=
NULL
;
subpicture_t
*
sub_first
=
NULL
;
subpicture_t
**
sub_last
=
&
sub_first
;
if
(
b
->
i_flags
&
(
BLOCK_FLAG_DISCONTINUITY
|
BLOCK_FLAG_CORRUPTED
))
goto
exit
;
while
(
b
->
i_buffer
>
3
)
{
const
int
table_id
=
b
->
p_buffer
[
0
];
if
(
table_id
!=
0xc6
)
{
//if (table_id != 0xff)
// msg_Err(dec, "Invalid SCTE-27 table id (0x%x)", table_id);
break
;
}
const
int
section_length
=
((
b
->
p_buffer
[
1
]
&
0xf
)
<<
8
)
|
b
->
p_buffer
[
2
];
if
(
section_length
<=
1
+
4
||
b
->
i_buffer
<
3
+
(
unsigned
)
section_length
)
{
msg_Err
(
dec
,
"Invalid SCTE-27 section length"
);
break
;
}
const
int
protocol_version
=
b
->
p_buffer
[
3
]
&
0x3f
;
if
(
protocol_version
!=
0
)
{
msg_Err
(
dec
,
"Unsupported SCTE-27 protocol version (%d)"
,
protocol_version
);
break
;
}
const
bool
segmentation_overlay
=
b
->
p_buffer
[
3
]
&
0x40
;
subpicture_t
*
sub
=
NULL
;
if
(
segmentation_overlay
)
{
if
(
section_length
<
1
+
5
+
4
)
break
;
int
id
=
GetWBE
(
&
b
->
p_buffer
[
4
]);
int
last
=
(
b
->
p_buffer
[
6
]
<<
4
)
|
(
b
->
p_buffer
[
7
]
>>
4
);
int
index
=
((
b
->
p_buffer
[
7
]
&
0x0f
)
<<
8
)
|
b
->
p_buffer
[
8
];
if
(
index
>
last
)
break
;
if
(
index
==
0
)
{
sys
->
segment_id
=
id
;
sys
->
segment_size
=
0
;
sys
->
segment_date
=
b
->
i_pts
>
VLC_TS_INVALID
?
b
->
i_pts
:
b
->
i_dts
;
}
else
{
if
(
sys
->
segment_id
!=
id
||
sys
->
segment_size
<=
0
)
{
sys
->
segment_id
=
-
1
;
break
;
}
}
int
segment_size
=
section_length
-
1
-
5
-
4
;
sys
->
segment_buffer
=
xrealloc
(
sys
->
segment_buffer
,
sys
->
segment_size
+
segment_size
);
memcpy
(
&
sys
->
segment_buffer
[
sys
->
segment_size
],
&
b
->
p_buffer
[
9
],
segment_size
);
sys
->
segment_size
+=
segment_size
;
if
(
index
==
last
)
{
sub
=
DecodeSubtitleMessage
(
dec
,
sys
->
segment_buffer
,
sys
->
segment_size
,
sys
->
segment_date
);
sys
->
segment_size
=
0
;
}
}
else
{
sub
=
DecodeSubtitleMessage
(
dec
,
&
b
->
p_buffer
[
4
],
section_length
-
1
-
4
,
b
->
i_pts
>
VLC_TS_INVALID
?
b
->
i_pts
:
b
->
i_dts
);
}
*
sub_last
=
sub
;
if
(
*
sub_last
)
sub_last
=
&
(
*
sub_last
)
->
p_next
;
b
->
i_buffer
-=
3
+
section_length
;
b
->
p_buffer
+=
3
+
section_length
;
break
;
}
exit:
block_Release
(
b
);
return
sub_first
;
}
static
int
Open
(
vlc_object_t
*
object
)
{
decoder_t
*
dec
=
(
decoder_t
*
)
object
;
if
(
dec
->
fmt_in
.
i_codec
!=
VLC_CODEC_SCTE_27
)
return
VLC_EGENERIC
;
decoder_sys_t
*
sys
=
dec
->
p_sys
=
malloc
(
sizeof
(
*
sys
));
if
(
!
sys
)
return
VLC_ENOMEM
;
sys
->
segment_id
=
-
1
;
sys
->
segment_size
=
0
;
sys
->
segment_buffer
=
NULL
;
dec
->
pf_decode_sub
=
Decode
;
es_format_Init
(
&
dec
->
fmt_out
,
SPU_ES
,
VLC_CODEC_SPU
);
dec
->
fmt_out
.
video
.
i_chroma
=
VLC_CODEC_YUVP
;
return
VLC_SUCCESS
;
}
static
void
Close
(
vlc_object_t
*
object
)
{
decoder_t
*
dec
=
(
decoder_t
*
)
object
;
decoder_sys_t
*
sys
=
dec
->
p_sys
;
free
(
sys
->
segment_buffer
);
free
(
sys
);
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment