1
mirror of https://github.com/mpv-player/mpv synced 2024-07-11 23:47:56 +02:00

Merge branch 'sub_mess'

This branch heavily refactors the subtitle code (both loading and
rendering), and adds support for a few new formats through FFmpeg.

We don't remove any of the old code yet. There are still some subtleties
related to subreader.c to be resolved: code page detection & conversion,
timing post-processing, UTF-16 subtitle support, support for the -subfps
option. Also, SRT reading and loading ASS via libass should be turned
into proper demuxers. (SRT is needed because Libav's is gravely broken,
and we want ASS loading via libass to cover full libass format support.
Both should be demuxers which are probed _before_ libavformat, so that
all subtitles can be loaded through the demuxer infrastructure, and
libavformat subtitles don't need to be treated in a special way.)
This commit is contained in:
wm4 2013-06-04 00:29:29 +02:00
commit 92ae48db0f
42 changed files with 1787 additions and 1542 deletions

View File

@ -93,13 +93,11 @@
- ``--aspect=16:9`` or ``--aspect=1.7777``
--ass, --no-ass
Render ASS subtitles natively, and convert text subtitles in other formats
to ASS internally (enabled by default).
Render ASS subtitles natively (enabled by default).
If ``--no-ass`` is specified, all subtitles are converted to plain text
internally. All tags and style declarations are stripped and ignored. The
subtitle renderer uses the font style as specified by the ``--sub-text-``
options instead.
If ``--no-ass`` is specified, all tags and style declarations are stripped
and ignored on display. The subtitle renderer uses the font style as
specified by the ``--sub-text-`` options instead.
*NOTE*: Using ``--no-ass`` may lead to incorrect or completely broken
rendering of ASS/SSA subtitles. It can sometimes be useful to forcibly
@ -1287,7 +1285,10 @@
See ``quit_watch_later`` input command.
--no-sub
Disables display of internal and external subtitles.
Don't select any subtitle when the file is loaded.
--no-sub-visibility
Disable display of subtitles, but still select and decode them.
--no-video
Do not play video. With some demuxers this may not work. In those cases

View File

@ -206,6 +206,7 @@ SOURCES = talloc.c \
demux/demux_mf.c \
demux/demux_mkv.c \
demux/demux_mpg.c \
demux/demux_sub.c \
demux/demux_ts.c \
demux/mp3_hdr.c \
demux/parse_es.c \
@ -228,13 +229,16 @@ SOURCES = talloc.c \
stream/url.c \
sub/dec_sub.c \
sub/draw_bmp.c \
sub/find_sub.c \
sub/find_subfiles.c \
sub/img_convert.c \
sub/sd_lavc.c \
sub/sd_lavc_conv.c \
sub/sd_microdvd.c \
sub/sd_movtext.c \
sub/sd_spu.c \
sub/sd_srt.c \
sub/spudec.c \
sub/sub.c \
sub/subassconvert.c \
sub/subreader.c \
video/csputils.c \
video/fmt-conversion.c \

View File

@ -402,13 +402,10 @@ static int decode_new_packet(struct sh_audio *sh)
}
AVPacket pkt;
av_init_packet(&pkt);
mp_set_av_packet(&pkt, mpkt);
pkt.data = start;
pkt.size = insize;
if (mpkt && mpkt->avpacket) {
pkt.side_data = mpkt->avpacket->side_data;
pkt.side_data_elems = mpkt->avpacket->side_data_elems;
}
if (pts != MP_NOPTS_VALUE && !packet_already_used) {
sh->pts = pts;
sh->pts_bytes = 0;

12
configure vendored
View File

@ -2684,6 +2684,17 @@ else
fi
echocheck "libavcodec AV_CODEC_PROP_TEXT_SUB API"
_avcodec_has_text_flag_api=no
statement_check libavcodec/avcodec.h 'int x = AV_CODEC_PROP_TEXT_SUB' && _avcodec_has_text_flag_api=yes
if test "$_avcodec_has_text_flag_api" = yes ; then
def_avcodec_has_text_flag_api='#define HAVE_AV_CODEC_PROP_TEXT_SUB 1'
else
def_avcodec_has_text_flag_api='#define HAVE_AV_CODEC_PROP_TEXT_SUB 0'
fi
echores "$_avcodec_has_text_flag_api"
echocheck "libavutil QP API"
_avutil_has_qp_api=no
statement_check libavutil/frame.h 'av_frame_get_qp_table(NULL, NULL, NULL)' && _avutil_has_qp_api=yes
@ -3268,6 +3279,7 @@ $def_zlib
$def_avutil_has_refcounting
$def_avutil_has_qp_api
$def_avcodec_has_text_flag_api
$def_libpostproc
$def_libavdevice
$def_libavfilter

View File

@ -18,8 +18,10 @@
#include <assert.h>
#include <libavutil/common.h>
#include <libavcodec/avcodec.h>
#include "core/mp_talloc.h"
#include "demux/demux_packet.h"
#include "av_common.h"
#include "codecs.h"
@ -58,6 +60,24 @@ void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st)
avctx->bits_per_coded_sample = st->bits_per_coded_sample;
}
// Set dst from mpkt. Note that dst is not refcountable.
// mpkt can be NULL to generate empty packets (used to flush delayed data).
// Does not set pts or duration fields.
void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt)
{
av_init_packet(dst);
dst->data = mpkt ? mpkt->buffer : NULL;
dst->size = mpkt ? mpkt->len : 0;
/* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info
* from demuxer. */
if (mpkt && mpkt->keyframe)
dst->flags |= AV_PKT_FLAG_KEY;
if (mpkt && mpkt->avpacket) {
dst->side_data = mpkt->avpacket->side_data;
dst->side_data_elems = mpkt->avpacket->side_data_elems;
}
}
void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type)
{
AVCodec *cur = NULL;

View File

@ -22,8 +22,10 @@
#include <libavcodec/avcodec.h>
struct mp_decoder_list;
struct demux_packet;
void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st);
void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt);
void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type);
int mp_codec_to_av_codec_id(const char *codec);
const char *mp_codec_from_av_codec_id(int codec_id);

View File

@ -472,17 +472,18 @@ const m_option_t common_opts[] = {
OPT_STRINGLIST("sub", sub_name, 0),
OPT_PATHLIST("sub-paths", sub_paths, 0),
{"subcp", &sub_cp, CONF_TYPE_STRING, 0, 0, 0, NULL},
{"sub-delay", &sub_delay, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL},
{"subfps", &sub_fps, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL},
OPT_STRING("subcp", sub_cp, 0),
OPT_FLOAT("sub-delay", sub_delay, 0),
OPT_FLOAT("subfps", sub_fps, 0),
OPT_FLAG("autosub", sub_auto, 0),
OPT_FLAG("sub-visibility", sub_visibility, 0),
OPT_FLAG("sub-forced-only", forced_subs_only, 0),
// enable Closed Captioning display
{"overlapsub", &suboverlap_enabled, CONF_TYPE_FLAG, 0, 0, 2, NULL},
{"sub-no-text-pp", &sub_no_text_pp, CONF_TYPE_FLAG, 0, 0, 1, NULL},
{"autosub-match", &sub_match_fuzziness, CONF_TYPE_CHOICE, 0,
M_CHOICES(({"exact", 0}, {"fuzzy", 1}, {"all", 2}))},
{"sub-pos", &sub_pos, CONF_TYPE_INT, CONF_RANGE, 0, 100, NULL},
OPT_FLAG_CONSTANTS("overlapsub", suboverlap_enabled, 0, 0, 2),
OPT_FLAG_STORE("sub-no-text-pp", sub_no_text_pp, 0, 1),
OPT_CHOICE("autosub-match", sub_match_fuzziness, 0,
({"exact", 0}, {"fuzzy", 1}, {"all", 2})),
OPT_INTRANGE("sub-pos", sub_pos, 0, 0, 100),
OPT_FLOATRANGE("sub-gauss", sub_gauss, 0, 0.0, 3.0),
OPT_FLAG("sub-gray", sub_gray, 0),
OPT_FLAG("ass", ass_enabled, 0),

View File

@ -50,7 +50,6 @@
#include "audio/filter/af.h"
#include "video/decode/dec_video.h"
#include "audio/decode/dec_audio.h"
#include "sub/spudec.h"
#include "core/path.h"
#include "sub/ass_mp.h"
#include "stream/tv.h"
@ -1273,11 +1272,12 @@ static int mp_property_sub(m_option_t *prop, int action, void *arg,
static int mp_property_sub_delay(m_option_t *prop, int action, void *arg,
MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
if (!mpctx->sh_video)
return M_PROPERTY_UNAVAILABLE;
switch (action) {
case M_PROPERTY_PRINT:
*(char **)arg = format_delay(sub_delay);
*(char **)arg = format_delay(opts->sub_delay);
return M_PROPERTY_OK;
}
return mp_property_generic_option(prop, action, arg, mpctx);
@ -1286,56 +1286,16 @@ static int mp_property_sub_delay(m_option_t *prop, int action, void *arg,
static int mp_property_sub_pos(m_option_t *prop, int action, void *arg,
MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
if (!mpctx->sh_video)
return M_PROPERTY_UNAVAILABLE;
if (action == M_PROPERTY_PRINT) {
*(char **)arg = talloc_asprintf(NULL, "%d/100", sub_pos);
*(char **)arg = talloc_asprintf(NULL, "%d/100", opts->sub_pos);
return M_PROPERTY_OK;
}
return property_osd_helper(prop, action, arg, mpctx);
}
/// Subtitle visibility (RW)
static int mp_property_sub_visibility(m_option_t *prop, int action,
void *arg, MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
if (!mpctx->sh_video)
return M_PROPERTY_UNAVAILABLE;
switch (action) {
case M_PROPERTY_SET:
opts->sub_visibility = *(int *)arg;
vo_osd_changed(OSDTYPE_SUBTITLE);
if (vo_spudec)
vo_osd_changed(OSDTYPE_SPU);
return M_PROPERTY_OK;
case M_PROPERTY_GET:
*(int *)arg = opts->sub_visibility;
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
/// Show only forced subtitles (RW)
static int mp_property_sub_forced_only(m_option_t *prop, int action,
void *arg, MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
if (!vo_spudec)
return M_PROPERTY_UNAVAILABLE;
if (action == M_PROPERTY_SET) {
opts->forced_subs_only = *(int *)arg;
spudec_set_forced_subs_only(vo_spudec, opts->forced_subs_only);
return M_PROPERTY_OK;
}
return mp_property_generic_option(prop, action, arg, mpctx);
}
#ifdef CONFIG_TV
static tvi_handle_t *get_tvh(struct MPContext *mpctx)
@ -1513,9 +1473,8 @@ static const m_option_t mp_properties[] = {
M_OPTION_PROPERTY_CUSTOM("sid", mp_property_sub),
M_OPTION_PROPERTY_CUSTOM("sub-delay", mp_property_sub_delay),
M_OPTION_PROPERTY_CUSTOM("sub-pos", mp_property_sub_pos),
{ "sub-visibility", mp_property_sub_visibility, CONF_TYPE_FLAG,
M_OPT_RANGE, 0, 1, NULL },
M_OPTION_PROPERTY_CUSTOM("sub-forced-only", mp_property_sub_forced_only),
M_OPTION_PROPERTY_CUSTOM("sub-visibility", property_osd_helper),
M_OPTION_PROPERTY_CUSTOM("sub-forced-only", property_osd_helper),
M_OPTION_PROPERTY_CUSTOM("sub-scale", property_osd_helper),
#ifdef CONFIG_ASS
M_OPTION_PROPERTY_CUSTOM("ass-use-margins", property_osd_helper),
@ -1991,26 +1950,18 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
}
case MP_CMD_SUB_STEP:
if (sh_video) {
int movement = cmd->args[0].v.i;
struct track *track = mpctx->current_track[STREAM_SUB];
bool available = false;
if (track && track->subdata) {
available = true;
step_sub(track->subdata, mpctx->video_pts, movement);
}
#ifdef CONFIG_ASS
struct ass_track *ass_track = sub_get_ass_track(mpctx->osd);
if (mpctx->osd->dec_sub) {
int movement = cmd->args[0].v.i;
struct ass_track *ass_track = sub_get_ass_track(mpctx->osd->dec_sub);
if (ass_track) {
available = true;
sub_delay += ass_step_sub(ass_track,
(mpctx->video_pts + sub_delay) * 1000 + .5, movement) / 1000.;
}
#endif
if (available)
set_osd_tmsg(mpctx, OSD_MSG_SUB_DELAY, osdl, osd_duration,
"Sub delay: %d ms", ROUND(sub_delay * 1000));
"Sub delay: %d ms", ROUND(opts->sub_delay * 1000));
double cur = (mpctx->video_pts + opts->sub_delay) * 1000 + .5;
opts->sub_delay += ass_step_sub(ass_track, cur, movement) / 1000.;
}
}
#endif
break;
case MP_CMD_OSD: {
@ -2194,7 +2145,6 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
if (tv_channel_list) {
set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration,
"Channel: %s", tv_channel_current->name);
//vo_osd_changed(OSDTYPE_SUBTITLE);
}
}
#ifdef CONFIG_PVR
@ -2232,7 +2182,6 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
if (tv_channel_list) {
set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration,
"Channel: %s", tv_channel_current->name);
//vo_osd_changed(OSDTYPE_SUBTITLE);
}
}
#ifdef CONFIG_PVR
@ -2265,7 +2214,6 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
if (tv_channel_list) {
set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration,
"Channel: %s", tv_channel_current->name);
//vo_osd_changed(OSDTYPE_SUBTITLE);
}
}
#ifdef CONFIG_PVR

View File

@ -74,6 +74,7 @@ void set_default_mplayer_options(struct MPOpts *opts)
.sub_id = -1,
.audio_display = 1,
.sub_visibility = 1,
.sub_pos = 100,
.extension_parsing = 1,
.audio_output_channels = MP_CHMAP_INIT_STEREO,
.audio_output_format = -1, // AF_FORMAT_UNKNOWN
@ -89,6 +90,7 @@ void set_default_mplayer_options(struct MPOpts *opts)
.ass_vsfilter_aspect_compat = 1,
.ass_style_override = 1,
.use_embedded_fonts = 1,
.suboverlap_enabled = 1,
.hwdec_codecs = "all",

View File

@ -22,8 +22,6 @@
#include <stdbool.h>
#include "core/options.h"
#include "sub/subreader.h"
#include "sub/find_subfiles.h"
#include "audio/mixer.h"
#include "demux/demux.h"
@ -33,7 +31,6 @@
#define INITIALIZED_AO 2
#define INITIALIZED_VOL 4
#define INITIALIZED_GETCH2 8
#define INITIALIZED_SPUDEC 32
#define INITIALIZED_STREAM 64
#define INITIALIZED_DEMUXER 512
#define INITIALIZED_ACODEC 1024
@ -106,15 +103,9 @@ struct track {
// Invariant: (!demuxer && !stream) || stream->demuxer == demuxer
struct sh_stream *stream;
// NOTE: demuxer subtitles, i.e. if stream!=NULL, do not use the following
// fields. The data is stored in stream->sub this case.
// External text subtitle using libass subtitle renderer.
// The sh_sub is a dummy and doesn't belong to a demuxer.
struct sh_sub *sh_sub;
// External text subtitle using non-libass subtitle renderer.
struct sub_data *subdata;
// For external subtitles, which are read fully on init. Do not attempt
// to read packets from them.
bool preloaded;
};
enum {
@ -129,7 +120,6 @@ typedef struct MPContext {
struct osd_state *osd;
struct mp_osd_msg *osd_msg_stack;
char *terminal_osd_text;
subtitle subs; // subtitle list used when reading subtitles from demuxer
int add_osd_seek_info; // bitfield of enum mp_osd_seek_info
double osd_visible; // for the osd bar only
@ -299,7 +289,6 @@ extern int forced_subs_only;
void uninit_player(struct MPContext *mpctx, unsigned int mask);
void reinit_audio_chain(struct MPContext *mpctx);
void init_vo_spudec(struct MPContext *mpctx);
double playing_audio_pts(struct MPContext *mpctx);
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename,
float fps, int noerr);

View File

@ -74,6 +74,7 @@
#include "sub/subreader.h"
#include "sub/find_subfiles.h"
#include "sub/dec_sub.h"
#include "sub/sd.h"
#include "core/mp_osd.h"
#include "video/out/vo.h"
@ -94,8 +95,6 @@
#include "core/codecs.h"
#include "sub/spudec.h"
#include "osdep/getch2.h"
#include "osdep/timer.h"
@ -282,10 +281,6 @@ static void print_stream(struct MPContext *mpctx, struct track *t)
if (t->title)
mp_msg(MSGT_CPLAYER, MSGL_INFO, " '%s'", t->title);
const char *codec = s ? s->codec : NULL;
if (!codec && t->sh_sub) // external subs hack
codec = t->sh_sub->gsh->codec;
if (!codec && t->subdata)
codec = t->subdata->codec;
mp_msg(MSGT_CPLAYER, MSGL_INFO, " (%s)", codec ? codec : "<unknown>");
if (t->is_external)
mp_msg(MSGT_CPLAYER, MSGL_INFO, " (external)");
@ -462,8 +457,10 @@ static void uninit_subs(struct demuxer *demuxer)
{
for (int i = 0; i < MAX_S_STREAMS; i++) {
struct sh_sub *sh = demuxer->s_streams[i];
if (sh && sh->initialized)
sub_uninit(sh);
if (sh) {
sub_destroy(sh->dec_sub);
sh->dec_sub = NULL;
}
}
}
@ -485,14 +482,10 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
if (mask & INITIALIZED_SUB) {
mpctx->initialized_flags &= ~INITIALIZED_SUB;
struct track *track = mpctx->current_track[STREAM_SUB];
// One of these was active; they can't be both active.
assert(!(mpctx->sh_sub && track && track->sh_sub));
if (mpctx->sh_sub)
sub_switchoff(mpctx->sh_sub, mpctx->osd);
if (track && track->sh_sub)
sub_switchoff(track->sh_sub, mpctx->osd);
sub_reset(mpctx->sh_sub->dec_sub);
cleanup_demux_stream(mpctx, STREAM_SUB);
mpctx->osd->dec_sub = NULL;
reset_subtitles(mpctx);
}
@ -555,12 +548,6 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
getch2_disable();
}
if (mask & INITIALIZED_SPUDEC) {
mpctx->initialized_flags &= ~INITIALIZED_SPUDEC;
spudec_free(vo_spudec);
vo_spudec = NULL;
}
if (mask & INITIALIZED_VOL) {
mpctx->initialized_flags &= ~INITIALIZED_VOL;
if (mpctx->mixer.ao) {
@ -1048,96 +1035,67 @@ static void add_dvd_tracks(struct MPContext *mpctx)
#endif
}
#ifdef CONFIG_ASS
static int free_sub_data(void *ptr)
{
struct sh_sub *sh_sub = *(struct sh_sub **)ptr;
if (sh_sub->track)
ass_free_track(sh_sub->track);
talloc_free(sh_sub->sub_data);
return 1;
}
#endif
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename,
float fps, int noerr)
{
struct MPOpts *opts = &mpctx->opts;
struct ass_track *asst = NULL;
sub_data *subd = NULL;
struct sh_sub *sh = NULL;
if (filename == NULL)
return NULL;
if (opts->ass_enabled) {
// Note: no text subtitles without libass. This is mainly because sd_ass is
// used for rendering. Even when showing subtitles with term-osd, going
// through sd_ass makes the code much simpler, as sd_ass can handle all
// the weird special-cases.
#ifdef CONFIG_ASS
struct ass_track *asst = mp_ass_read_stream(mpctx->ass_library,
filename, sub_cp);
bool is_native_ass = asst;
const char *codec = NULL;
if (!asst) {
subd = sub_read_file(filename, fps, &mpctx->opts);
if (subd) {
codec = subd->codec;
asst = mp_ass_read_subdata(mpctx->ass_library, opts, subd, fps);
talloc_free(subd);
subd = NULL;
}
}
if (asst) {
sh = sd_ass_create_from_track(asst, is_native_ass, opts);
if (codec)
sh->gsh->codec = codec;
}
#endif
} else
asst = mp_ass_read_stream(mpctx->ass_library, filename, opts->sub_cp);
if (!asst)
subd = sub_read_file(filename, fps, &mpctx->opts);
if (asst || subd) {
struct demuxer *d = new_sub_pseudo_demuxer(opts);
assert(d->num_streams == 1);
struct sh_stream *s = d->streams[0];
assert(s->type == STREAM_SUB);
s->codec = asst ? "ass" : subd->codec;
s->sub->track = asst;
s->sub->sub_data = subd;
if (!sh && !subd) {
struct track *ext = open_external_file(mpctx, filename, NULL, 0,
STREAM_SUB);
if (ext)
return ext;
mp_tmsg(MSGT_CPLAYER, noerr ? MSGL_WARN : MSGL_ERR,
"Cannot load subtitles: %s\n", filename);
return NULL;
}
struct sh_sub **pptr = talloc(d, struct sh_sub*);
*pptr = s->sub;
talloc_set_destructor(pptr, free_sub_data);
struct track *track = talloc_ptrtype(NULL, track);
*track = (struct track) {
.type = STREAM_SUB,
.title = talloc_strdup(track, filename),
.user_tid = find_new_tid(mpctx, STREAM_SUB),
.demuxer_id = -1,
.is_external = true,
.sh_sub = talloc_steal(track, sh),
.subdata = talloc_steal(track, subd),
.external_filename = talloc_strdup(track, filename),
};
MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
return track;
}
void init_vo_spudec(struct MPContext *mpctx)
{
uninit_player(mpctx, INITIALIZED_SPUDEC);
unsigned width, height;
// we currently can't work without video stream
if (!mpctx->sh_video)
return;
width = mpctx->sh_video->disp_w;
height = mpctx->sh_video->disp_h;
#ifdef CONFIG_DVDREAD
if (vo_spudec == NULL && mpctx->stream->type == STREAMTYPE_DVD) {
vo_spudec = spudec_new_scaled(((dvd_priv_t *)(mpctx->stream->priv))->
cur_pgc->palette, width, height, NULL, 0);
struct track *t = add_stream_track(mpctx, s, false);
t->is_external = true;
t->preloaded = true;
t->title = talloc_strdup(t, filename);
t->external_filename = talloc_strdup(t, filename);
MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, d);
return t;
}
#endif
if (vo_spudec == NULL && mpctx->sh_sub) {
sh_sub_t *sh = mpctx->sh_sub;
vo_spudec = spudec_new_scaled(NULL, width, height, sh->extradata,
sh->extradata_len);
}
// Used with libavformat subtitles.
struct track *ext = open_external_file(mpctx, filename, NULL, 0, STREAM_SUB);
if (ext)
return ext;
if (vo_spudec != NULL) {
mpctx->initialized_flags |= INITIALIZED_SPUDEC;
mp_property_do("sub-forced-only", M_PROPERTY_SET,
&mpctx->opts.forced_subs_only, mpctx);
}
mp_tmsg(MSGT_CPLAYER, noerr ? MSGL_WARN : MSGL_ERR,
"Cannot load subtitles: %s\n", filename);
return NULL;
}
int mp_get_cache_percent(struct MPContext *mpctx)
@ -1433,7 +1391,7 @@ static mp_osd_msg_t *get_osd_msg(struct MPContext *mpctx)
if (mpctx->osd_visible && now >= mpctx->osd_visible) {
mpctx->osd_visible = 0;
mpctx->osd->progbar_type = -1; // disable
vo_osd_changed(OSDTYPE_PROGBAR);
osd_changed(mpctx->osd, OSDTYPE_PROGBAR);
}
if (mpctx->osd_function_visible && now >= mpctx->osd_function_visible) {
mpctx->osd_function_visible = 0;
@ -1492,7 +1450,7 @@ void set_osd_bar(struct MPContext *mpctx, int type, const char *name,
mpctx->osd->progbar_type = type;
mpctx->osd->progbar_value = (val - min) / (max - min);
mpctx->osd->progbar_num_stops = 0;
vo_osd_changed(OSDTYPE_PROGBAR);
osd_changed(mpctx->osd, OSDTYPE_PROGBAR);
return;
}
@ -1509,7 +1467,7 @@ static void update_osd_bar(struct MPContext *mpctx, int type,
float new_value = (val - min) / (max - min);
if (new_value != mpctx->osd->progbar_value) {
mpctx->osd->progbar_value = new_value;
vo_osd_changed(OSDTYPE_PROGBAR);
osd_changed(mpctx->osd, OSDTYPE_PROGBAR);
}
}
}
@ -1545,25 +1503,20 @@ void set_osd_function(struct MPContext *mpctx, int osd_function)
/**
* \brief Display text subtitles on the OSD
*/
void set_osd_subtitle(struct MPContext *mpctx, subtitle *subs)
static void set_osd_subtitle(struct MPContext *mpctx, const char *text)
{
int i;
vo_sub = subs;
vo_osd_changed(OSDTYPE_SUBTITLE);
if (!mpctx->sh_video) {
// reverse order, since newest set_osd_msg is displayed first
for (i = SUB_MAX_TEXT - 1; i >= 0; i--) {
if (!subs || i >= subs->lines || !subs->text[i])
rm_osd_msg(mpctx, OSD_MSG_SUB_BASE + i);
else {
// HACK: currently display time for each sub line
// except the last is set to 2 seconds.
int display_time = i == subs->lines - 1 ? 180000 : 2000;
set_osd_msg(mpctx, OSD_MSG_SUB_BASE + i, 1, display_time,
"%s", subs->text[i]);
}
if (!text)
text = "";
if (strcmp(mpctx->osd->sub_text, text) != 0) {
osd_set_sub(mpctx->osd, text);
if (!mpctx->sh_video) {
rm_osd_msg(mpctx, OSD_MSG_SUB_BASE);
if (text && text[0])
set_osd_msg(mpctx, OSD_MSG_SUB_BASE, 1, INT_MAX, "%s", text);
}
}
if (!text[0])
rm_osd_msg(mpctx, OSD_MSG_SUB_BASE);
}
// sym == mpctx->osd_function
@ -1876,149 +1829,79 @@ static bool is_non_interleaved(struct MPContext *mpctx, struct track *track)
static void reset_subtitles(struct MPContext *mpctx)
{
if (mpctx->sh_sub)
sub_reset(mpctx->sh_sub, mpctx->osd);
sub_clear_text(&mpctx->subs, MP_NOPTS_VALUE);
if (vo_sub)
set_osd_subtitle(mpctx, NULL);
if (vo_spudec) {
spudec_reset(vo_spudec);
vo_osd_changed(OSDTYPE_SPU);
}
sub_reset(mpctx->sh_sub->dec_sub);
set_osd_subtitle(mpctx, NULL);
osd_changed(mpctx->osd, OSDTYPE_SUB);
}
static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
{
struct MPOpts *opts = &mpctx->opts;
struct sh_video *sh_video = mpctx->sh_video;
struct sh_sub *sh_sub = mpctx->sh_sub;
struct demux_stream *d_sub = sh_sub ? sh_sub->ds : NULL;
unsigned char *packet = NULL;
int len;
const char *type = sh_sub ? sh_sub->gsh->codec : NULL;
mpctx->osd->sub_offset = mpctx->video_offset;
struct track *track = mpctx->current_track[STREAM_SUB];
if (!track)
if (!(mpctx->initialized_flags & INITIALIZED_SUB))
return;
if (!track->under_timeline)
mpctx->osd->sub_offset = 0;
struct track *track = mpctx->current_track[STREAM_SUB];
struct sh_sub *sh_sub = mpctx->sh_sub;
assert(track && sh_sub);
struct dec_sub *dec_sub = sh_sub->dec_sub;
double refpts_s = refpts_tl - mpctx->osd->sub_offset;
double curpts_s = refpts_s + sub_delay;
double video_offset = track->under_timeline ? mpctx->video_offset : 0;
// find sub
if (track->subdata) {
if (sub_fps == 0)
sub_fps = sh_video ? sh_video->fps : 25;
find_sub(mpctx, track->subdata, curpts_s *
(track->subdata->sub_uses_time ? 100. : sub_fps));
}
mpctx->osd->sub_offset = video_offset - opts->sub_delay;
// DVD sub:
if (is_dvd_sub(type) && !(sh_sub && sh_sub->active)) {
int timestamp;
// Get a sub packet from the demuxer (or the vobsub.c thing, which
// should be a demuxer, but isn't).
while (1) {
// Vobsub
len = 0;
{
// DVD sub
assert(d_sub->sh == sh_sub);
len = ds_get_packet_sub(d_sub, (unsigned char **)&packet);
if (len > 0) {
// XXX This is wrong, sh_video->pts can be arbitrarily
// much behind demuxing position. Unfortunately using
// d_video->pts which would have been the simplest
// improvement doesn't work because mpeg specific hacks
// in video.c set d_video->pts to 0.
float x = d_sub->pts - refpts_s;
if (x > -20 && x < 20) // prevent missing subs on pts reset
timestamp = 90000 * d_sub->pts;
else
timestamp = 90000 * curpts_s;
mp_dbg(MSGT_CPLAYER, MSGL_V, "\rDVD sub: len=%d "
"v_pts=%5.3f s_pts=%5.3f ts=%d \n", len,
refpts_s, d_sub->pts, timestamp);
}
}
if (len <= 0 || !packet)
break;
// create it only here, since with some broken demuxers we might
// type = v but no DVD sub and we currently do not change the
// "original frame size" ever after init, leading to wrong-sized
// PGS subtitles.
if (!vo_spudec)
vo_spudec = spudec_new(NULL);
if (timestamp >= 0)
spudec_assemble(vo_spudec, packet, len, timestamp);
}
} else if (d_sub && (is_text_sub(type) || (sh_sub && sh_sub->active))) {
double curpts_s = refpts_tl - mpctx->osd->sub_offset;
double refpts_s = refpts_tl - video_offset;
if (!track->preloaded) {
struct demux_stream *d_sub = sh_sub->ds;
bool non_interleaved = is_non_interleaved(mpctx, track);
if (non_interleaved)
ds_get_next_pts(d_sub);
while (d_sub->first) {
while (1) {
if (non_interleaved)
ds_get_next_pts(d_sub);
if (!d_sub->first)
break;
double subpts_s = ds_get_next_pts(d_sub);
if (subpts_s == MP_NOPTS_VALUE) {
// Try old method of getting PTS. This is only needed in the
// DVD playback case with demux_mpg.
// XXX This is wrong, sh_video->pts can be arbitrarily
// much behind demuxing position. Unfortunately using
// d_video->pts which would have been the simplest
// improvement doesn't work because mpeg specific hacks
// in video.c set d_video->pts to 0.
float x = d_sub->pts - refpts_s;
if (x > -20 && x < 20) // prevent missing subs on pts reset
subpts_s = d_sub->pts;
else
subpts_s = curpts_s;
}
if (subpts_s > curpts_s) {
mp_dbg(MSGT_CPLAYER, MSGL_DBG2,
"Sub early: c_pts=%5.3f s_pts=%5.3f\n",
curpts_s, subpts_s);
// Libass handled subs can be fed to it in advance
if (!opts->ass_enabled || !is_text_sub(type))
if (!sub_accept_packets_in_advance(dec_sub))
break;
// Try to avoid demuxing whole file at once
if (non_interleaved && subpts_s > curpts_s + 1)
break;
}
double duration = d_sub->first->duration;
len = ds_get_packet_sub(d_sub, &packet);
struct demux_packet pkt;
struct demux_packet *orig = ds_get_packet_sub(d_sub);
if (!orig)
break;
pkt = *orig;
pkt.pts = subpts_s;
mp_dbg(MSGT_CPLAYER, MSGL_V, "Sub: c_pts=%5.3f s_pts=%5.3f "
"duration=%5.3f len=%d\n", curpts_s, subpts_s, duration,
len);
if (type && strcmp(type, "mov_text") == 0) {
if (len < 2)
continue;
len = FFMIN(len - 2, AV_RB16(packet));
packet += 2;
}
if (sh_sub && sh_sub->active) {
sub_decode(sh_sub, mpctx->osd, packet, len, subpts_s, duration);
} else if (subpts_s != MP_NOPTS_VALUE) {
// text sub
if (duration < 0)
sub_clear_text(&mpctx->subs, MP_NOPTS_VALUE);
if (is_ass_sub(type)) { // ssa/ass subs without libass => convert to plaintext
int i;
unsigned char *p = packet;
for (i = 0; i < 8 && *p != '\0'; p++)
if (*p == ',')
i++;
if (*p == '\0') /* Broken line? */
continue;
len -= p - packet;
packet = p;
}
double endpts_s = MP_NOPTS_VALUE;
if (subpts_s != MP_NOPTS_VALUE && duration >= 0)
endpts_s = subpts_s + duration;
sub_add_text(&mpctx->subs, packet, len, endpts_s);
set_osd_subtitle(mpctx, &mpctx->subs);
}
if (non_interleaved)
ds_get_next_pts(d_sub);
"duration=%5.3f len=%d\n", curpts_s, pkt.pts, pkt.duration,
pkt.len);
sub_decode(dec_sub, &pkt);
}
if (!opts->ass_enabled)
if (sub_clear_text(&mpctx->subs, curpts_s))
set_osd_subtitle(mpctx, &mpctx->subs);
}
if (vo_spudec) {
spudec_heartbeat(vo_spudec, 90000 * curpts_s);
if (spudec_changed(vo_spudec))
vo_osd_changed(OSDTYPE_SPU);
}
if (!mpctx->osd->render_bitmap_subs || !mpctx->sh_video)
set_osd_subtitle(mpctx, sub_get_text(dec_sub, curpts_s));
}
static int check_framedrop(struct MPContext *mpctx, double frame_time)
@ -2061,11 +1944,11 @@ static double timing_sleep(struct MPContext *mpctx, double time_frame)
return time_frame;
}
static void set_dvdsub_fake_extradata(struct sh_sub *sh_sub, struct stream *st,
struct sh_video *sh_video)
static void set_dvdsub_fake_extradata(struct dec_sub *dec_sub, struct stream *st,
int width, int height)
{
#ifdef CONFIG_DVDREAD
if (st->type != STREAMTYPE_DVD || !sh_video)
if (st->type != STREAMTYPE_DVD)
return;
struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
@ -2074,10 +1957,13 @@ static void set_dvdsub_fake_extradata(struct sh_sub *sh_sub, struct stream *st,
float cmatrix[3][4];
mp_get_yuv2rgb_coeffs(&csp, cmatrix);
int width = sh_video->disp_w;
int height = sh_video->disp_h;
int *palette = ((dvd_priv_t *)st->priv)->cur_pgc->palette;
if (width == 0 || height == 0) {
width = 720;
height = 480;
}
char *s = NULL;
s = talloc_asprintf_append(s, "size: %dx%d\n", width, height);
s = talloc_asprintf_append(s, "palette: ");
@ -2093,9 +1979,7 @@ static void set_dvdsub_fake_extradata(struct sh_sub *sh_sub, struct stream *st,
}
s = talloc_asprintf_append(s, "\n");
free(sh_sub->extradata);
sh_sub->extradata = strdup(s);
sh_sub->extradata_len = strlen(s);
sub_set_extradata(dec_sub, s, strlen(s));
talloc_free(s);
#endif
}
@ -2108,10 +1992,12 @@ static void reinit_subs(struct MPContext *mpctx)
assert(!(mpctx->initialized_flags & INITIALIZED_SUB));
init_demux_stream(mpctx, STREAM_SUB);
if (!track)
if (!mpctx->sh_sub)
return;
if (!mpctx->sh_sub->dec_sub)
mpctx->sh_sub->dec_sub = sub_create(opts);
if (track->demuxer && !track->stream) {
// Lazily added DVD track - we must not miss the first subtitle packet,
// which makes the demuxer create the sh_stream, and contains the first
@ -2127,32 +2013,32 @@ static void reinit_subs(struct MPContext *mpctx)
return;
}
assert(track->demuxer && track->stream);
mpctx->initialized_flags |= INITIALIZED_SUB;
if (track->subdata || track->sh_sub) {
#ifdef CONFIG_ASS
if (opts->ass_enabled && track->sh_sub)
sub_init(track->sh_sub, mpctx->osd);
#endif
vo_osd_changed(OSDTYPE_SUBTITLE);
} else if (track->stream) {
struct stream *s = track->demuxer ? track->demuxer->stream : NULL;
if (s && s->type == STREAMTYPE_DVD)
set_dvdsub_fake_extradata(mpctx->sh_sub, s, mpctx->sh_video);
// lavc dvdsubdec doesn't read color/resolution on Libav 9.1 and below
// Don't use it for new ffmpeg; spudec can't handle ffmpeg .idx demuxing
// (ffmpeg added .idx demuxing during lavc 54.79.100)
bool broken_lavc = false;
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 40, 0)
broken_lavc = true;
#endif
if (is_dvd_sub(mpctx->sh_sub->gsh->codec) && track->demuxer
&& (track->demuxer->type == DEMUXER_TYPE_MPEG_PS || broken_lavc))
init_vo_spudec(mpctx);
else
sub_init(mpctx->sh_sub, mpctx->osd);
struct sh_sub *sh_sub = mpctx->sh_sub;
struct dec_sub *dec_sub = sh_sub->dec_sub;
assert(dec_sub);
if (!sub_is_initialized(dec_sub)) {
int w = mpctx->sh_video ? mpctx->sh_video->disp_w : 0;
int h = mpctx->sh_video ? mpctx->sh_video->disp_h : 0;
set_dvdsub_fake_extradata(dec_sub, track->demuxer->stream, w, h);
sub_set_video_res(dec_sub, w, h);
sub_set_ass_renderer(dec_sub, mpctx->osd->ass_library,
mpctx->osd->ass_renderer);
sub_init_from_sh(dec_sub, sh_sub);
}
mpctx->osd->dec_sub = dec_sub;
// Decides whether to use OSD path or normal subtitle rendering path.
mpctx->osd->render_bitmap_subs =
opts->ass_enabled || !sub_has_get_text(dec_sub);
reset_subtitles(mpctx);
}
static char *track_layout_hash(struct MPContext *mpctx)
@ -2597,7 +2483,7 @@ int reinit_video_chain(struct MPContext *mpctx)
mpctx->delay = 0;
mpctx->vo_pts_history_seek_ts++;
// ========== Init display (sh_video->disp_w*sh_video->disp_h/out_fmt) ============
reset_subtitles(mpctx);
return 1;
@ -4552,7 +4438,6 @@ terminate_playback: // don't jump here after ao/vo/getch initialization!
talloc_free(mpctx->resolve_result);
mpctx->resolve_result = NULL;
vo_sub = NULL;
#ifdef CONFIG_ASS
if (mpctx->osd->ass_renderer)
ass_renderer_done(mpctx->osd->ass_renderer);

View File

@ -25,9 +25,6 @@
struct MPContext;
struct MPOpts;
struct subtitle;
void set_osd_subtitle(struct MPContext *mpctx, struct subtitle *subs);
struct mp_resolve_result {
char *url;

View File

@ -83,7 +83,6 @@ typedef struct MPOpts {
int osd_level;
int osd_duration;
int osd_fractions;
char *vobsub_name;
int untimed;
char *stream_capture;
char *stream_dump;
@ -140,9 +139,17 @@ typedef struct MPOpts {
char **sub_lang;
int audio_display;
int sub_visibility;
int sub_pos;
float sub_delay;
float sub_fps;
int forced_subs_only;
char *quvi_format;
// subreader.c
int suboverlap_enabled;
char *sub_cp;
int sub_no_text_pp;
char *audio_stream;
int audio_stream_cache;
char *sub_stream;
@ -170,6 +177,7 @@ typedef struct MPOpts {
char **sub_name;
char **sub_paths;
int sub_auto;
int sub_match_fuzziness;
int osd_bar_visible;
float osd_bar_align_x;
float osd_bar_align_y;

View File

@ -66,6 +66,7 @@ extern const demuxer_desc_t demuxer_desc_mpeg_es;
extern const demuxer_desc_t demuxer_desc_mpeg4_es;
extern const demuxer_desc_t demuxer_desc_h264_es;
extern const demuxer_desc_t demuxer_desc_mpeg_ts;
extern const demuxer_desc_t demuxer_desc_sub;
/* Please do not add any new demuxers here. If you want to implement a new
* demuxer, add it to libavformat, except for wrappers around external
@ -95,6 +96,8 @@ const demuxer_desc_t *const demuxer_list[] = {
&demuxer_desc_mpeg_ts,
// auto-probe last, because it checks file-extensions only
&demuxer_desc_mf,
// no auto-probe
&demuxer_desc_sub,
/* Please do not add any new demuxers here. If you want to implement a new
* demuxer, add it to libavformat, except for wrappers around external
* libraries and demuxers requiring binary support. */
@ -217,8 +220,8 @@ static const demuxer_desc_t *get_demuxer_desc_from_type(int file_format)
}
demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type,
int a_id, int v_id, int s_id, char *filename)
static demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type,
int a_id, int v_id, int s_id, char *filename)
{
struct demuxer *d = talloc_zero(NULL, struct demuxer);
d->stream = stream;
@ -248,6 +251,18 @@ demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type,
return d;
}
// for demux_sub.c
demuxer_t *new_sub_pseudo_demuxer(struct MPOpts *opts)
{
struct stream *s = open_stream("null://", NULL, NULL);
assert(s);
struct demuxer *d = new_demuxer(opts, s, DEMUXER_TYPE_SUB,
-1, -1, -1, NULL);
new_sh_stream(d, STREAM_SUB);
talloc_steal(d, s);
return d;
}
static struct sh_stream *new_sh_stream_id(demuxer_t *demuxer,
enum stream_type type,
int stream_index,
@ -786,20 +801,20 @@ int ds_get_packet_pts(demux_stream_t *ds, unsigned char **start, double *pts)
return len;
}
int ds_get_packet_sub(demux_stream_t *ds, unsigned char **start)
struct demux_packet *ds_get_packet_sub(demux_stream_t *ds)
{
int len;
if (ds->buffer_pos >= ds->buffer_size) {
*start = NULL;
if (!ds->packs)
return -1; // no sub
return NULL; // no sub
if (!ds_fill_buffer(ds))
return -1; // EOF
return NULL; // EOF
}
len = ds->buffer_size - ds->buffer_pos;
*start = &ds->buffer[ds->buffer_pos];
ds->buffer_pos += len;
return len;
if (ds->buffer_pos < ds->buffer_size) {
ds->current->buffer += ds->buffer_pos;
ds->buffer_size -= ds->buffer_pos;
}
ds->buffer_pos = ds->buffer_size;
return ds->current;
}
struct demux_packet *ds_get_packet2(struct demux_stream *ds, bool repeat_last)

View File

@ -77,6 +77,7 @@ enum demuxer_type {
DEMUXER_TYPE_END,
DEMUXER_TYPE_PLAYLIST,
DEMUXER_TYPE_SUB,
};
enum timestamp_type {
@ -304,9 +305,9 @@ static inline void *realloc_struct(void *ptr, size_t nmemb, size_t size)
return realloc(ptr, nmemb * size);
}
struct demuxer *new_demuxer(struct MPOpts *opts, struct stream *stream,
int type, int a_id, int v_id, int s_id,
char *filename);
demuxer_t *new_sub_pseudo_demuxer(struct MPOpts *opts);
void free_demuxer(struct demuxer *demuxer);
void demuxer_add_packet(demuxer_t *demuxer, struct sh_stream *stream,
@ -343,7 +344,7 @@ void ds_free_packs(struct demux_stream *ds);
int ds_get_packet(struct demux_stream *ds, unsigned char **start);
int ds_get_packet_pts(struct demux_stream *ds, unsigned char **start,
double *pts);
int ds_get_packet_sub(struct demux_stream *ds, unsigned char **start);
struct demux_packet *ds_get_packet_sub(demux_stream_t *ds);
struct demux_packet *ds_get_packet2(struct demux_stream *ds, bool repeat_last);
double ds_get_next_pts(struct demux_stream *ds);
int ds_parse(struct demux_stream *sh, uint8_t **buffer, int *len, double pts,

View File

@ -671,11 +671,8 @@ static int demux_lavf_fill_buffer(demuxer_t *demux, demux_stream_t *dsds)
if (ts != AV_NOPTS_VALUE) {
dp->pts = ts * av_q2d(st->time_base);
priv->last_pts = dp->pts * AV_TIME_BASE;
// always set duration for subtitles, even if AV_PKT_FLAG_KEY isn't set,
// otherwise they will stay on screen to long if e.g. ASS is demuxed
// from mkv
if ((stream->type == STREAM_SUB || (pkt->flags & AV_PKT_FLAG_KEY)) &&
pkt->convergence_duration > 0)
dp->duration = pkt->duration * av_q2d(st->time_base);
if (pkt->convergence_duration > 0)
dp->duration = pkt->convergence_duration * av_q2d(st->time_base);
}
dp->pos = demux->filepos;

View File

@ -523,7 +523,7 @@ static int demux_mpg_read_packet(demuxer_t *demux,int id){
if(!demux->s_streams[aid]){
sh_sub_t *sh = new_sh_sub(demux, aid);
if (sh) sh->gsh->codec = "dvd_subtitle";
if (sh) sh->gsh->codec = "dvd_subtitle_mpg";
mp_msg(MSGT_DEMUX,MSGL_V,"==> Found subtitle: %d\n",aid);
}

38
demux/demux_sub.c Normal file
View File

@ -0,0 +1,38 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
// Note: not a real demuxer. The frontend has its own code to open subtitle
// code, and then creates a new dummy demuxer with new_sub_demuxer().
// But eventually, all subtitles should be opened this way, and this
// file can be removed.
#include "demux.h"
static int dummy_fill_buffer(struct demuxer *demuxer, struct demux_stream *ds)
{
return 0;
}
const struct demuxer_desc demuxer_desc_sub = {
.info = "External subtitles pseudo demuxer",
.name = "sub",
.shortdesc = "sub",
.author = "",
.comment = "",
.type = DEMUXER_TYPE_SUB,
.fill_buffer = dummy_fill_buffer,
};

View File

@ -161,10 +161,11 @@ typedef struct sh_video {
typedef struct sh_sub {
SH_COMMON
bool active; // after track switch decoder may stay initialized, not active
unsigned char *extradata; // extra header data passed from demuxer
int extradata_len;
const struct sd_functions *sd_driver;
struct ass_track *track; // loaded by libass
struct sub_data *sub_data; // loaded by subreader.c
struct dec_sub *dec_sub; // decoder context
} sh_sub_t;
// demuxer.c:

View File

@ -38,7 +38,9 @@
#include "stream/stream.h"
#include "core/options.h"
void mp_ass_set_style(ASS_Style *style, struct osd_style_opts *opts)
// res_y should be track->PlayResY
// It determines scaling of font sizes and more.
void mp_ass_set_style(ASS_Style *style, int res_y, struct osd_style_opts *opts)
{
if (opts->font) {
free(style->FontName);
@ -46,9 +48,9 @@ void mp_ass_set_style(ASS_Style *style, struct osd_style_opts *opts)
style->treat_fontname_as_pattern = 1;
}
// libass_font_size = FontSize * (window_height / MP_ASS_FONT_PLAYRESY)
// scale translates parameters from PlayResY=720 to MP_ASS_FONT_PLAYRESY
double scale = MP_ASS_FONT_PLAYRESY / 720.0;
// libass_font_size = FontSize * (window_height / res_y)
// scale translates parameters from PlayResY=720 to res_y
double scale = res_y / 720.0;
style->FontSize = opts->font_size * scale;
style->PrimaryColour = MP_ASS_COLOR(opts->color);
@ -74,129 +76,40 @@ void mp_ass_set_style(ASS_Style *style, struct osd_style_opts *opts)
#endif
}
ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts)
// Add default styles, if the track does not have any styles yet.
// Apply style overrides if the user provides any.
void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts)
{
ASS_Track *track = ass_new_track(library);
track->track_type = TRACK_TYPE_ASS;
track->Timer = 100.;
track->PlayResY = MP_ASS_FONT_PLAYRESY;
track->WrapStyle = 0;
if (opts->ass_styles_file && opts->ass_style_override)
ass_read_styles(track, opts->ass_styles_file, sub_cp);
ass_read_styles(track, opts->ass_styles_file, opts->sub_cp);
if (track->n_styles == 0) {
if (!track->PlayResY) {
track->PlayResY = MP_ASS_FONT_PLAYRESY;
track->PlayResX = track->PlayResY * 4 / 3;
}
track->Kerning = true;
int sid = ass_alloc_style(track);
track->default_style = sid;
ASS_Style *style = track->styles + sid;
style->Name = strdup("Default");
style->Alignment = 2;
mp_ass_set_style(style, opts->sub_text_style);
mp_ass_set_style(style, track->PlayResY, opts->sub_text_style);
}
if (opts->ass_style_override)
ass_process_force_style(track);
return track;
}
static int check_duplicate_plaintext_event(ASS_Track *track)
ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts)
{
int i;
ASS_Event *evt = track->events + track->n_events - 1;
ASS_Track *track = ass_new_track(library);
for (i = 0; i < track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with
if (track->events[i].Start == evt->Start &&
track->events[i].Duration == evt->Duration &&
strcmp(track->events[i].Text, evt->Text) == 0)
return 1;
return 0;
}
track->track_type = TRACK_TYPE_ASS;
track->Timer = 100.;
/**
* \brief Convert subtitle to ASS_Events for the given track
* \param track track
* \param sub subtitle to convert
* \return event id
* note: assumes that subtitle is _not_ fps-based; caller must manually correct
* Start and Duration in other case.
**/
static int ass_process_subtitle(ASS_Track *track, subtitle *sub)
{
int eid;
ASS_Event *event;
int len = 0, j;
char *p;
char *end;
mp_ass_add_default_styles(track, opts);
eid = ass_alloc_event(track);
event = track->events + eid;
event->Start = sub->start * 10;
event->Duration = (sub->end - sub->start) * 10;
event->Style = track->default_style;
for (j = 0; j < sub->lines; ++j)
len += sub->text[j] ? strlen(sub->text[j]) : 0;
len += 2 * sub->lines; // '\N', including the one after the last line
len += 6; // {\anX}
len += 1; // '\0'
event->Text = malloc(len);
end = event->Text + len;
p = event->Text;
if (sub->alignment)
p += snprintf(p, end - p, "{\\an%d}", sub->alignment);
for (j = 0; j < sub->lines; ++j)
p += snprintf(p, end - p, "%s\\N", sub->text[j]);
if (sub->lines > 0)
p -= 2; // remove last "\N"
*p = 0;
if (check_duplicate_plaintext_event(track)) {
ass_free_event(track, eid);
track->n_events--;
return -1;
}
mp_msg(MSGT_ASS, MSGL_V,
"plaintext event at %" PRId64 ", +%" PRId64 ": %s \n",
(int64_t) event->Start, (int64_t) event->Duration, event->Text);
return eid;
}
/**
* \brief Convert subdata to ASS_Track
* \param subdata subtitles struct from subreader
* \param fps video framerate
* \return newly allocated ASS_Track, filled with subtitles from subdata
*/
ASS_Track *mp_ass_read_subdata(ASS_Library *library, struct MPOpts *opts,
sub_data *subdata, double fps)
{
ASS_Track *track;
int i;
track = mp_ass_default_track(library, opts);
track->name = subdata->filename ? strdup(subdata->filename) : 0;
for (i = 0; i < subdata->sub_num; ++i) {
int eid = ass_process_subtitle(track, subdata->subtitles + i);
if (eid < 0)
continue;
if (!subdata->sub_uses_time) {
track->events[eid].Start *= 100. / fps;
track->events[eid].Duration *= 100. / fps;
}
}
return track;
}
@ -244,7 +157,7 @@ void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts,
if (opts->ass_style_override) {
set_use_margins = opts->ass_use_margins;
#if LIBASS_VERSION >= 0x01010000
set_sub_pos = 100 - sub_pos;
set_sub_pos = 100 - opts->sub_pos;
#endif
set_line_spacing = opts->ass_line_spacing;
set_font_scale = opts->sub_scale;

View File

@ -27,7 +27,8 @@
#include "config.h"
#include "subreader.h"
// font sizes and explicit tags in subassconvert.c assume this size (?)
// This is probably arbitrary.
// sd_lavc_conv might indirectly still assume this PlayResY, though.
#define MP_ASS_FONT_PLAYRESY 288
#define MP_ASS_RGBA(r, g, b, a) \
@ -44,11 +45,11 @@ struct MPOpts;
struct mp_osd_res;
struct osd_style_opts;
void mp_ass_set_style(ASS_Style *style, struct osd_style_opts *opts);
void mp_ass_set_style(ASS_Style *style, int res_y, struct osd_style_opts *opts);
void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts);
ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts);
ASS_Track *mp_ass_read_subdata(ASS_Library *library, struct MPOpts *opts,
sub_data *subdata, double fps);
ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname,
char *charset);

View File

@ -22,105 +22,351 @@
#include "config.h"
#include "demux/stheader.h"
#include "sub/sd.h"
#include "sub/sub.h"
#include "sub/dec_sub.h"
#include "sd.h"
#include "sub.h"
#include "dec_sub.h"
#include "subreader.h"
#include "core/options.h"
#include "core/mp_msg.h"
extern const struct sd_functions sd_ass;
extern const struct sd_functions sd_lavc;
extern const struct sd_functions sd_spu;
extern const struct sd_functions sd_movtext;
extern const struct sd_functions sd_srt;
extern const struct sd_functions sd_microdvd;
extern const struct sd_functions sd_lavc_conv;
bool is_text_sub(const char *t)
{
return t && (is_ass_sub(t) ||
strcmp(t, "text") == 0 ||
strcmp(t, "subrip") == 0 ||
strcmp(t, "mov_text") == 0);
}
bool is_ass_sub(const char *t)
{
return t && (strcmp(t, "ass") == 0 ||
strcmp(t, "ssa") == 0);
}
bool is_dvd_sub(const char *t)
{
return t && strcmp(t, "dvd_subtitle") == 0;
}
void sub_init(struct sh_sub *sh, struct osd_state *osd)
{
struct MPOpts *opts = sh->opts;
assert(!osd->sh_sub);
if (sd_lavc.probe(sh))
sh->sd_driver = &sd_lavc;
static const struct sd_functions *sd_list[] = {
#ifdef CONFIG_ASS
if (opts->ass_enabled && sd_ass.probe(sh))
sh->sd_driver = &sd_ass;
&sd_ass,
#endif
if (sh->sd_driver) {
if (sh->sd_driver->init(sh, osd) < 0)
&sd_lavc,
&sd_spu,
&sd_movtext,
&sd_srt,
&sd_microdvd,
&sd_lavc_conv,
NULL
};
#define MAX_NUM_SD 3
struct dec_sub {
struct MPOpts *opts;
struct sd init_sd;
struct sd *sd[MAX_NUM_SD];
int num_sd;
};
struct dec_sub *sub_create(struct MPOpts *opts)
{
struct dec_sub *sub = talloc_zero(NULL, struct dec_sub);
sub->opts = opts;
return sub;
}
static void sub_uninit(struct dec_sub *sub)
{
sub_reset(sub);
for (int n = 0; n < sub->num_sd; n++) {
if (sub->sd[n]->driver->uninit)
sub->sd[n]->driver->uninit(sub->sd[n]);
talloc_free(sub->sd[n]);
}
sub->num_sd = 0;
}
void sub_destroy(struct dec_sub *sub)
{
if (!sub)
return;
sub_uninit(sub);
talloc_free(sub);
}
bool sub_is_initialized(struct dec_sub *sub)
{
return !!sub->num_sd;
}
struct sd *sub_get_last_sd(struct dec_sub *sub)
{
return sub->num_sd ? sub->sd[sub->num_sd - 1] : NULL;
}
void sub_set_video_res(struct dec_sub *sub, int w, int h)
{
sub->init_sd.sub_video_w = w;
sub->init_sd.sub_video_h = h;
}
void sub_set_extradata(struct dec_sub *sub, void *data, int data_len)
{
sub->init_sd.extradata = data_len ? talloc_memdup(sub, data, data_len) : NULL;
sub->init_sd.extradata_len = data_len;
}
void sub_set_ass_renderer(struct dec_sub *sub, struct ass_library *ass_library,
struct ass_renderer *ass_renderer)
{
sub->init_sd.ass_library = ass_library;
sub->init_sd.ass_renderer = ass_renderer;
}
static void print_chain(struct dec_sub *sub)
{
mp_msg(MSGT_OSD, MSGL_V, "Subtitle filter chain: ");
for (int n = 0; n < sub->num_sd; n++) {
struct sd *sd = sub->sd[n];
mp_msg(MSGT_OSD, MSGL_V, "%s%s (%s)", n > 0 ? " -> " : "",
sd->driver->name, sd->codec);
}
mp_msg(MSGT_OSD, MSGL_V, "\n");
}
// Subtitles read with subreader.c
static void read_sub_data(struct dec_sub *sub, struct sub_data *subdata)
{
assert(sub_accept_packets_in_advance(sub));
char *temp = NULL;
struct sd *sd = sub_get_last_sd(sub);
sd->no_remove_duplicates = true;
for (int i = 0; i < subdata->sub_num; i++) {
subtitle *st = &subdata->subtitles[i];
// subdata is in 10 ms ticks, pts is in seconds
double t = subdata->sub_uses_time ? 0.01 : (1 / subdata->fallback_fps);
int len = 0;
for (int j = 0; j < st->lines; j++)
len += st->text[j] ? strlen(st->text[j]) : 0;
len += 2 * st->lines; // '\N', including the one after the last line
len += 6; // {\anX}
len += 1; // '\0'
if (talloc_get_size(temp) < len) {
talloc_free(temp);
temp = talloc_array(NULL, char, len);
}
char *p = temp;
char *end = p + len;
if (st->alignment)
p += snprintf(p, end - p, "{\\an%d}", st->alignment);
for (int j = 0; j < st->lines; j++)
p += snprintf(p, end - p, "%s\\N", st->text[j]);
if (st->lines > 0)
p -= 2; // remove last "\N"
*p = 0;
struct demux_packet pkt = {0};
pkt.pts = st->start * t;
pkt.duration = (st->end - st->start) * t;
pkt.buffer = temp;
pkt.len = strlen(temp);
sub_decode(sub, &pkt);
}
// Hack for broken FFmpeg packet format: make sd_ass keep the subtitle
// events on reset(), even though broken FFmpeg ASS packets were received
// (from sd_lavc_conv.c). Normally, these events are removed on seek/reset,
// but this is obviously unwanted in this case.
if (sd && sd->driver->fix_events)
sd->driver->fix_events(sd);
sd->no_remove_duplicates = false;
talloc_free(temp);
}
static int sub_init_decoder(struct dec_sub *sub, struct sd *sd)
{
sd->driver = NULL;
for (int n = 0; sd_list[n]; n++) {
if (sd_list[n]->supports_format(sd->codec)) {
sd->driver = sd_list[n];
break;
}
}
if (!sd->driver)
return -1;
if (sd->driver->init(sd) < 0)
return -1;
return 0;
}
void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh)
{
assert(!sub->num_sd);
if (sh->extradata && !sub->init_sd.extradata)
sub_set_extradata(sub, sh->extradata, sh->extradata_len);
struct sd init_sd = sub->init_sd;
init_sd.codec = sh->gsh->codec;
init_sd.ass_track = sh->track;
while (sub->num_sd < MAX_NUM_SD) {
struct sd *sd = talloc(NULL, struct sd);
*sd = init_sd;
sd->opts = sub->opts;
if (sub_init_decoder(sub, sd) < 0) {
talloc_free(sd);
break;
}
sub->sd[sub->num_sd] = sd;
sub->num_sd++;
// Try adding new converters until a decoder is reached
if (sd->driver->get_bitmaps || sd->driver->get_text) {
print_chain(sub);
if (sh->sub_data)
read_sub_data(sub, sh->sub_data);
return;
osd->sh_sub = sh;
osd->switch_sub_id++;
sh->initialized = true;
sh->active = true;
}
init_sd = (struct sd) {
.codec = sd->output_codec,
.converted_from = sd->codec,
.extradata = sd->output_extradata,
.extradata_len = sd->output_extradata_len,
.ass_library = sub->init_sd.ass_library,
.ass_renderer = sub->init_sd.ass_renderer,
};
}
sub_uninit(sub);
mp_msg(MSGT_OSD, MSGL_ERR, "Could not find subtitle decoder for format '%s'.\n",
sh->gsh->codec ? sh->gsh->codec : "<unknown>");
}
bool sub_accept_packets_in_advance(struct dec_sub *sub)
{
// Converters are assumed to always accept packets in advance
struct sd *sd = sub_get_last_sd(sub);
return sd && sd->driver->accept_packets_in_advance;
}
static void decode_next(struct dec_sub *sub, int n, struct demux_packet *packet)
{
struct sd *sd = sub->sd[n];
sd->driver->decode(sd, packet);
if (n + 1 >= sub->num_sd || !sd->driver->get_converted)
return;
while (1) {
struct demux_packet *next =
sd->driver->get_converted ? sd->driver->get_converted(sd) : NULL;
if (!next)
break;
decode_next(sub, n + 1, next);
}
}
void sub_decode(struct sh_sub *sh, struct osd_state *osd, void *data,
int data_len, double pts, double duration)
void sub_decode(struct dec_sub *sub, struct demux_packet *packet)
{
if (sh->active && sh->sd_driver->decode)
sh->sd_driver->decode(sh, osd, data, data_len, pts, duration);
if (sub->num_sd > 0)
decode_next(sub, 0, packet);
}
void sub_get_bitmaps(struct osd_state *osd, struct mp_osd_res dim, double pts,
void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res)
{
struct MPOpts *opts = osd->opts;
struct MPOpts *opts = sub->opts;
struct sd *sd = sub_get_last_sd(sub);
*res = (struct sub_bitmaps) {0};
if (!opts->sub_visibility || !osd->sh_sub || !osd->sh_sub->active) {
/* Change ID in case we just switched from visible subtitles
* to current state. Hopefully, unnecessarily claiming that
* things may have changed is harmless for empty contents.
* Increase osd-> values ahead so that _next_ returned id
* is also guaranteed to differ from this one.
*/
osd->switch_sub_id++;
} else {
if (osd->sh_sub->sd_driver->get_bitmaps)
osd->sh_sub->sd_driver->get_bitmaps(osd->sh_sub, osd, dim, pts, res);
if (sd && opts->sub_visibility) {
if (sd->driver->get_bitmaps)
sd->driver->get_bitmaps(sd, dim, pts, res);
}
res->bitmap_id += osd->switch_sub_id;
res->bitmap_pos_id += osd->switch_sub_id;
osd->switch_sub_id = 0;
}
void sub_reset(struct sh_sub *sh, struct osd_state *osd)
bool sub_has_get_text(struct dec_sub *sub)
{
if (sh->active && sh->sd_driver->reset)
sh->sd_driver->reset(sh, osd);
struct sd *sd = sub_get_last_sd(sub);
return sd && sd->driver->get_text;
}
void sub_switchoff(struct sh_sub *sh, struct osd_state *osd)
char *sub_get_text(struct dec_sub *sub, double pts)
{
if (sh->active && sh->sd_driver->switch_off) {
assert(osd->sh_sub == sh);
sh->sd_driver->switch_off(sh, osd);
osd->sh_sub = NULL;
struct MPOpts *opts = sub->opts;
struct sd *sd = sub_get_last_sd(sub);
char *text = NULL;
if (sd && opts->sub_visibility) {
if (sd->driver->get_text)
text = sd->driver->get_text(sd, pts);
}
sh->active = false;
return text;
}
void sub_uninit(struct sh_sub *sh)
void sub_reset(struct dec_sub *sub)
{
assert (!sh->active);
if (sh->initialized && sh->sd_driver->uninit)
sh->sd_driver->uninit(sh);
sh->initialized = false;
for (int n = 0; n < sub->num_sd; n++) {
if (sub->sd[n]->driver->reset)
sub->sd[n]->driver->reset(sub->sd[n]);
}
}
#define MAX_PACKETS 10
#define MAX_BYTES 10000
struct sd_conv_buffer {
struct demux_packet pkt[MAX_PACKETS];
int num_pkt;
int read_pkt;
char buffer[MAX_BYTES];
int cur_buffer;
};
void sd_conv_add_packet(struct sd *sd, void *data, int data_len, double pts,
double duration)
{
if (!sd->sd_conv_buffer)
sd->sd_conv_buffer = talloc_zero(sd, struct sd_conv_buffer);
struct sd_conv_buffer *buf = sd->sd_conv_buffer;
if (buf->num_pkt >= MAX_PACKETS || buf->cur_buffer + data_len + 1 > MAX_BYTES)
goto out_of_space;
if (buf->read_pkt == buf->num_pkt)
sd_conv_def_reset(sd);
assert(buf->read_pkt == 0); // no mixing of reading/adding allowed
struct demux_packet *pkt = &buf->pkt[buf->num_pkt++];
*pkt = (struct demux_packet) {
.buffer = &buf->buffer[buf->cur_buffer],
.len = data_len,
.pts = pts,
.duration = duration,
};
memcpy(pkt->buffer, data, data_len);
pkt->buffer[data_len] = 0;
buf->cur_buffer += data_len + 1;
return;
out_of_space:
mp_msg(MSGT_OSD, MSGL_ERR, "Subtitle too big.\n");
}
struct demux_packet *sd_conv_def_get_converted(struct sd *sd)
{
struct sd_conv_buffer *buf = sd->sd_conv_buffer;
if (buf && buf->read_pkt < buf->num_pkt)
return &buf->pkt[buf->read_pkt++];
return NULL;
}
void sd_conv_def_reset(struct sd *sd)
{
struct sd_conv_buffer *buf = sd->sd_conv_buffer;
if (buf) {
buf->read_pkt = buf->num_pkt = 0;
buf->cur_buffer = 0;
}
}

View File

@ -9,26 +9,36 @@
struct sh_sub;
struct ass_track;
struct MPOpts;
struct demux_packet;
struct ass_library;
struct ass_renderer;
bool is_text_sub(const char *t);
bool is_ass_sub(const char *t);
bool is_dvd_sub(const char *t);
struct dec_sub;
struct sd;
void sub_decode(struct sh_sub *sh, struct osd_state *osd, void *data,
int data_len, double pts, double duration);
void sub_get_bitmaps(struct osd_state *osd, struct mp_osd_res dim, double pts,
struct dec_sub *sub_create(struct MPOpts *opts);
void sub_destroy(struct dec_sub *sub);
void sub_set_video_res(struct dec_sub *sub, int w, int h);
void sub_set_extradata(struct dec_sub *sub, void *data, int data_len);
void sub_set_ass_renderer(struct dec_sub *sub, struct ass_library *ass_library,
struct ass_renderer *ass_renderer);
void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh);
bool sub_is_initialized(struct dec_sub *sub);
bool sub_accept_packets_in_advance(struct dec_sub *sub);
void sub_decode(struct dec_sub *sub, struct demux_packet *packet);
void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res);
void sub_init(struct sh_sub *sh, struct osd_state *osd);
void sub_reset(struct sh_sub *sh, struct osd_state *osd);
void sub_switchoff(struct sh_sub *sh, struct osd_state *osd);
void sub_uninit(struct sh_sub *sh);
bool sub_has_get_text(struct dec_sub *sub);
char *sub_get_text(struct dec_sub *sub, double pts);
void sub_reset(struct dec_sub *sub);
struct sh_sub *sd_ass_create_from_track(struct ass_track *track,
bool vsfilter_aspect,
struct MPOpts *opts);
struct sd *sub_get_last_sd(struct dec_sub *sub);
#ifdef CONFIG_ASS
struct ass_track *sub_get_ass_track(struct osd_state *osd);
struct ass_track *sub_get_ass_track(struct dec_sub *sub);
#endif
#endif

View File

@ -1,174 +0,0 @@
/*
* .SUB
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <stdio.h>
#include "sub.h"
#include "subreader.h"
#include "core/mp_msg.h"
#include "core/mp_common.h"
#include "core/mplayer.h"
static int current_sub=0;
//static subtitle* subtitles=NULL;
static int nosub_range_start=-1;
static int nosub_range_end=-1;
static const sub_data *last_sub_data = NULL;
void step_sub(sub_data *subd, float pts, int movement) {
subtitle *subs;
int key;
if (subd == NULL) return;
subs = subd->subtitles;
key = (pts+sub_delay) * (subd->sub_uses_time ? 100 : sub_fps);
/* Tell the OSD subsystem that the OSD contents will change soon */
vo_osd_changed(OSDTYPE_SUBTITLE);
/* If we are moving forward, don't count the next (current) subtitle
* if we haven't displayed it yet. Same when moving other direction.
*/
if (movement > 0 && key < subs[current_sub].start)
movement--;
if (movement < 0 && key >= subs[current_sub].end)
movement++;
/* Never move beyond first or last subtitle. */
if (current_sub+movement < 0)
movement = 0-current_sub;
if (current_sub+movement >= subd->sub_num)
movement = subd->sub_num - current_sub - 1;
current_sub += movement;
sub_delay = subs[current_sub].start / (subd->sub_uses_time ? 100 : sub_fps) - pts;
}
void find_sub(struct MPContext *mpctx, sub_data* subd,int key){
subtitle *subs;
subtitle *new_sub = NULL;
int i,j;
if ( !subd || subd->sub_num == 0) return;
subs = subd->subtitles;
if (last_sub_data != subd) {
// Sub data changed, reset nosub range.
last_sub_data = subd;
nosub_range_start = -1;
nosub_range_end = -1;
}
if(vo_sub){
if(key>=vo_sub->start && key<=vo_sub->end) return; // OK!
} else {
if(key>nosub_range_start && key<nosub_range_end) return; // OK!
}
// sub changed!
/* Tell the OSD subsystem that the OSD contents will change soon */
vo_osd_changed(OSDTYPE_SUBTITLE);
if(key<=0){
// no sub here
goto update;
}
// printf("\r---- sub changed ----\n");
// check next sub.
if(current_sub>=0 && current_sub+1 < subd->sub_num){
if(key>subs[current_sub].end && key<subs[current_sub+1].start){
// no sub
nosub_range_start=subs[current_sub].end;
nosub_range_end=subs[current_sub+1].start;
goto update;
}
// next sub?
++current_sub;
new_sub=&subs[current_sub];
if(key>=new_sub->start && key<=new_sub->end) goto update; // OK!
}
// printf("\r---- sub log search... ----\n");
// use logarithmic search:
i=0;
j = subd->sub_num - 1;
// printf("Searching %d in %d..%d\n",key,subs[i].start,subs[j].end);
while(j>=i){
current_sub=(i+j+1)/2;
new_sub=&subs[current_sub];
if(key<new_sub->start) j=current_sub-1;
else if(key>new_sub->end) i=current_sub+1;
else goto update; // found!
}
// if(key>=new_sub->start && key<=new_sub->end) return; // OK!
// check where are we...
if(key<new_sub->start){
if(current_sub<=0){
// before the first sub
nosub_range_start=key-1; // tricky
nosub_range_end=new_sub->start;
// printf("FIRST... key=%d end=%d \n",key,new_sub->start);
new_sub=NULL;
goto update;
}
--current_sub;
if(key>subs[current_sub].end && key<subs[current_sub+1].start){
// no sub
nosub_range_start=subs[current_sub].end;
nosub_range_end=subs[current_sub+1].start;
// printf("No sub... 1 \n");
new_sub=NULL;
goto update;
}
printf("HEH???? ");
} else {
if(key<=new_sub->end) printf("JAJJ! "); else
if(current_sub+1 >= subd->sub_num){
// at the end?
nosub_range_start=new_sub->end;
nosub_range_end=0x7FFFFFFF; // MAXINT
// printf("END!?\n");
new_sub=NULL;
goto update;
} else
if(key>subs[current_sub].end && key<subs[current_sub+1].start){
// no sub
nosub_range_start=subs[current_sub].end;
nosub_range_end=subs[current_sub+1].start;
// printf("No sub... 2 \n");
new_sub=NULL;
goto update;
}
}
mp_msg(MSGT_FIXME,MSGL_FIXME,"SUB ERROR: %d ? %d --- %d [%d] \n",key,(int)new_sub->start,(int)new_sub->end,current_sub);
new_sub=NULL; // no sub here
update:
set_osd_subtitle(mpctx, new_sub);
}

View File

@ -12,7 +12,6 @@
#include "core/mp_common.h"
#include "sub/find_subfiles.h"
#include "sub/sub.h"
#include "sub/subreader.h"
static struct bstr strip_ext(struct bstr str)
{
@ -119,9 +118,9 @@ static void append_dir_subtitles(struct MPOpts *opts,
// does it end with a subtitle extension?
#ifdef CONFIG_ICONV
#ifdef CONFIG_ENCA
int i = (sub_cp && strncasecmp(sub_cp, "enca", 4) != 0) ? 3 : 0;
int i = (opts->sub_cp && strncasecmp(opts->sub_cp, "enca", 4) != 0) ? 3 : 0;
#else
int i = sub_cp ? 3 : 0;
int i = opts->sub_cp ? 3 : 0;
#endif
#else
int i = 0;
@ -153,12 +152,12 @@ static void append_dir_subtitles(struct MPOpts *opts,
if (!prio && bstrcmp(tmp_fname_trim, f_fname_trim) == 0)
prio = 3; // matches the movie name
if (!prio && bstr_find(tmp_fname_trim, f_fname_trim) >= 0
&& sub_match_fuzziness >= 1)
&& opts->sub_match_fuzziness >= 1)
prio = 2; // contains the movie name
if (!prio) {
// doesn't contain the movie name
// don't try in the mplayer subtitle directory
if (!limit_fuzziness && sub_match_fuzziness >= 2) {
if (!limit_fuzziness && opts->sub_match_fuzziness >= 2) {
prio = 1;
}
}

View File

@ -89,7 +89,7 @@ static void create_osd_ass_track(struct osd_state *osd, struct osd_object *obj)
ASS_Style *style = track->styles + sid;
style->Alignment = 5; // top-title, left
style->Name = strdup("OSD");
mp_ass_set_style(style, osd->opts->osd_style);
mp_ass_set_style(style, MP_ASS_FONT_PLAYRESY, osd->opts->osd_style);
// Set to neutral base direction, as opposed to VSFilter LTR default
style->Encoding = -1;
}
@ -158,7 +158,7 @@ static void update_osd(struct osd_state *osd, struct osd_object *obj)
font.font_size *= opts->osd_scale;
ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style;
mp_ass_set_style(style, &font);
mp_ass_set_style(style, obj->osd_track->PlayResY, &font);
char *text = mangle_ass(osd->osd_text);
add_osd_ass_event(obj->osd_track, text);
@ -354,7 +354,7 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj)
clear_obj(obj);
if (!(vo_sub && opts->sub_visibility))
if (!osd->sub_text || !osd->sub_text[0])
return;
if (!obj->osd_track)
@ -364,21 +364,15 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj)
font.font_size *= opts->sub_scale;
ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style;
mp_ass_set_style(style, &font);
mp_ass_set_style(style, obj->osd_track->PlayResY, &font);
#if LIBASS_VERSION >= 0x01010000
ass_set_line_position(osd->osd_render, 100 - sub_pos);
ass_set_line_position(osd->osd_render, 100 - opts->sub_pos);
#endif
char *text = talloc_strdup(NULL, "");
for (int n = 0; n < vo_sub->lines; n++)
text = talloc_asprintf_append_buffer(text, "%s\n", vo_sub->text[n]);
char *escaped_text = mangle_ass(text);
char *escaped_text = mangle_ass(osd->sub_text);
add_osd_ass_event(obj->osd_track, escaped_text);
talloc_free(escaped_text);
talloc_free(text);
}
static void update_object(struct osd_state *osd, struct osd_object *obj)
@ -387,7 +381,7 @@ static void update_object(struct osd_state *osd, struct osd_object *obj)
case OSDTYPE_OSD:
update_osd(osd, obj);
break;
case OSDTYPE_SUBTITLE:
case OSDTYPE_SUBTEXT:
update_sub(osd, obj);
break;
case OSDTYPE_PROGBAR:

View File

@ -2,18 +2,74 @@
#define MPLAYER_SD_H
#include "dec_sub.h"
#include "demux/demux_packet.h"
struct sd_functions {
bool (*probe)(struct sh_sub *sh);
int (*init)(struct sh_sub *sh, struct osd_state *osd);
void (*decode)(struct sh_sub *sh, struct osd_state *osd,
void *data, int data_len, double pts, double duration);
void (*get_bitmaps)(struct sh_sub *sh, struct osd_state *osd,
struct mp_osd_res dim, double pts,
struct sub_bitmaps *res);
void (*reset)(struct sh_sub *sh, struct osd_state *osd);
void (*switch_off)(struct sh_sub *sh, struct osd_state *osd);
void (*uninit)(struct sh_sub *sh);
struct sd {
struct MPOpts *opts;
const struct sd_functions *driver;
void *priv;
const char *codec;
// Extra header data passed from demuxer
char *extradata;
int extradata_len;
// Set to !=NULL if the input packets are being converted from another
// format.
const char *converted_from;
// Video resolution used for subtitle decoding. Doesn't necessarily match
// the resolution of the VO, nor does it have to be the OSD resolution.
int sub_video_w, sub_video_h;
// Make sd_ass use an existing track
struct ass_track *ass_track;
// Shared renderer for ASS - done to avoid reloading embedded fonts.
struct ass_library *ass_library;
struct ass_renderer *ass_renderer;
// If false, try to remove multiple subtitles.
// (Only for decoders which have accept_packets_in_advance set.)
bool no_remove_duplicates;
// Set by sub converter
const char *output_codec;
char *output_extradata;
int output_extradata_len;
// Internal buffer for sd_conv_* functions
struct sd_conv_buffer *sd_conv_buffer;
};
struct sd_functions {
const char *name;
bool accept_packets_in_advance;
bool (*supports_format)(const char *format);
int (*init)(struct sd *sd);
void (*decode)(struct sd *sd, struct demux_packet *packet);
void (*reset)(struct sd *sd);
void (*uninit)(struct sd *sd);
void (*fix_events)(struct sd *sd);
// decoder
void (*get_bitmaps)(struct sd *sd, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res);
char *(*get_text)(struct sd *sd, double pts);
// converter
struct demux_packet *(*get_converted)(struct sd *sd);
};
void sd_conv_add_packet(struct sd *sd, void *data, int data_len, double pts,
double duration);
struct demux_packet *sd_conv_def_get_converted(struct sd *sd);
void sd_conv_def_reset(struct sd *sd);
void sd_conv_def_uninit(struct sd *sd);
#define SD_MAX_LINE_LEN 1000
#endif

View File

@ -17,21 +17,20 @@
*/
#include <stdlib.h>
#include <ass/ass.h>
#include <assert.h>
#include <string.h>
#include <ass/ass.h>
#include "talloc.h"
#include "core/options.h"
#include "core/mp_common.h"
#include "core/mp_msg.h"
#include "demux/stheader.h"
#include "sub.h"
#include "dec_sub.h"
#include "ass_mp.h"
#include "sd.h"
#include "subassconvert.h"
struct sd_ass_priv {
struct ass_track *ass_track;
@ -39,11 +38,20 @@ struct sd_ass_priv {
bool incomplete_event;
struct sub_bitmap *parts;
bool flush_on_seek;
char last_text[500];
};
static bool probe(struct sh_sub *sh)
static bool is_native_ass(const char *t)
{
return is_text_sub(sh->gsh->codec);
return strcmp(t, "ass") == 0 || strcmp(t, "ssa") == 0;
}
static bool supports_format(const char *format)
{
// ass-text is produced by converters and the subreader.c ssa parser; this
// format has ASS tags, but doesn't start with any prelude, nor does it
// have extradata.
return format && (is_native_ass(format) || strcmp(format, "ass-text") == 0);
}
static void free_last_event(ASS_Track *track)
@ -53,37 +61,45 @@ static void free_last_event(ASS_Track *track)
track->n_events--;
}
static int init(struct sh_sub *sh, struct osd_state *osd)
static int init(struct sd *sd)
{
struct sd_ass_priv *ctx;
bool ass = is_ass_sub(sh->gsh->codec);
struct MPOpts *opts = sd->opts;
if (!sd->ass_library || !sd->ass_renderer)
return -1;
if (sh->initialized) {
ctx = sh->context;
bool is_converted = sd->converted_from != NULL;
struct sd_ass_priv *ctx = talloc_zero(NULL, struct sd_ass_priv);
sd->priv = ctx;
if (sd->ass_track) {
ctx->ass_track = sd->ass_track;
} else {
ctx = talloc_zero(NULL, struct sd_ass_priv);
sh->context = ctx;
if (ass) {
ctx->ass_track = ass_new_track(osd->ass_library);
if (sh->extradata)
ass_process_codec_private(ctx->ass_track, sh->extradata,
sh->extradata_len);
} else
ctx->ass_track = mp_ass_default_track(osd->ass_library, sh->opts);
ctx->ass_track = ass_new_track(sd->ass_library);
if (!is_converted)
ctx->ass_track->track_type = TRACK_TYPE_ASS;
}
ctx->vsfilter_aspect = ass;
if (sd->extradata) {
ass_process_codec_private(ctx->ass_track, sd->extradata,
sd->extradata_len);
}
mp_ass_add_default_styles(ctx->ass_track, opts);
ctx->vsfilter_aspect = !is_converted;
return 0;
}
static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
int data_len, double pts, double duration)
static void decode(struct sd *sd, struct demux_packet *packet)
{
void *data = packet->buffer;
int data_len = packet->len;
double pts = packet->pts;
double duration = packet->duration;
unsigned char *text = data;
struct sd_ass_priv *ctx = sh->context;
struct sd_ass_priv *ctx = sd->priv;
ASS_Track *track = ctx->ass_track;
if (is_ass_sub(sh->gsh->codec)) {
if (is_native_ass(sd->codec)) {
if (bstr_startswith0((bstr){data, data_len}, "Dialogue: ")) {
// broken ffmpeg ASS packet format
ctx->flush_on_seek = true;
@ -121,13 +137,13 @@ static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
return;
}
not_all_whitespace:;
char buf[500];
subassconvert_subrip(text, buf, sizeof(buf));
for (int i = 0; i < track->n_events; i++)
if (track->events[i].Start == ipts
&& (duration <= 0 || track->events[i].Duration == iduration)
&& strcmp(track->events[i].Text, buf) == 0)
return; // We've already added this subtitle
if (!sd->no_remove_duplicates) {
for (int i = 0; i < track->n_events; i++)
if (track->events[i].Start == ipts
&& (duration <= 0 || track->events[i].Duration == iduration)
&& strcmp(track->events[i].Text, text) == 0)
return; // We've already added this subtitle
}
if (duration <= 0) {
iduration = 10000;
ctx->incomplete_event = true;
@ -137,17 +153,16 @@ static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
event->Start = ipts;
event->Duration = iduration;
event->Style = track->default_style;
event->Text = strdup(buf);
event->Text = strdup(text);
}
static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
struct mp_osd_res dim, double pts,
static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res)
{
struct sd_ass_priv *ctx = sh->context;
struct MPOpts *opts = osd->opts;
struct sd_ass_priv *ctx = sd->priv;
struct MPOpts *opts = sd->opts;
if (pts == MP_NOPTS_VALUE)
if (pts == MP_NOPTS_VALUE || !sd->ass_renderer)
return;
double scale = dim.display_par;
@ -155,7 +170,7 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
? opts->ass_vsfilter_aspect_compat : 1;
if (ctx->vsfilter_aspect && use_vs_aspect)
scale = scale * dim.video_par;
ASS_Renderer *renderer = osd->ass_renderer;
ASS_Renderer *renderer = sd->ass_renderer;
mp_ass_configure(renderer, opts, &dim);
ass_set_aspect_ratio(renderer, scale, 1);
mp_ass_render_frame(renderer, ctx->ass_track, pts * 1000 + .5,
@ -163,9 +178,110 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
talloc_steal(ctx, ctx->parts);
}
static void reset(struct sh_sub *sh, struct osd_state *osd)
struct buf {
char *start;
int size;
int len;
};
static void append(struct buf *b, char c)
{
struct sd_ass_priv *ctx = sh->context;
if (b->len < b->size) {
b->start[b->len] = c;
b->len++;
}
}
static void ass_to_plaintext(struct buf *b, const char *in)
{
bool in_tag = false;
bool in_drawing = false;
while (*in) {
if (in_tag) {
if (in[0] == '}') {
in += 1;
in_tag = false;
} else if (in[0] == '\\' && in[1] == 'p') {
in += 2;
// skip text between \pN and \p0 tags
if (in[0] == '0') {
in_drawing = false;
} else if (in[0] >= '1' && in[0] <= '9') {
in_drawing = true;
}
} else {
in += 1;
}
} else {
if (in[0] == '\\' && (in[1] == 'N' || in[1] == 'n')) {
in += 2;
append(b, '\n');
} else if (in[0] == '\\' && in[1] == 'h') {
in += 2;
append(b, ' ');
} else if (in[0] == '{') {
in += 1;
in_tag = true;
} else {
if (!in_drawing)
append(b, in[0]);
in += 1;
}
}
}
}
// Empty string counts as whitespace. Reads s[len-1] even if there are \0s.
static bool is_whitespace_only(char *s, int len)
{
for (int n = 0; n < len; n++) {
if (s[n] != ' ' && s[n] != '\t')
return false;
}
return true;
}
static char *get_text(struct sd *sd, double pts)
{
struct sd_ass_priv *ctx = sd->priv;
ASS_Track *track = ctx->ass_track;
if (pts == MP_NOPTS_VALUE)
return NULL;
struct buf b = {ctx->last_text, sizeof(ctx->last_text) - 1};
for (int i = 0; i < track->n_events; ++i) {
ASS_Event *event = track->events + i;
double start = event->Start / 1000.0;
double end = (event->Start + event->Duration) / 1000.0;
if (pts >= start && pts < end) {
if (event->Text) {
int start = b.len;
ass_to_plaintext(&b, event->Text);
if (!is_whitespace_only(&b.start[b.len], b.len - start))
append(&b, '\n');
}
}
}
b.start[b.len] = '\0';
if (b.len > 0 && b.start[b.len - 1] == '\n')
b.start[b.len - 1] = '\0';
return ctx->last_text;
}
static void fix_events(struct sd *sd)
{
struct sd_ass_priv *ctx = sd->priv;
ctx->flush_on_seek = false;
}
static void reset(struct sd *sd)
{
struct sd_ass_priv *ctx = sd->priv;
if (ctx->incomplete_event)
free_last_event(ctx->ass_track);
ctx->incomplete_event = false;
@ -174,56 +290,33 @@ static void reset(struct sh_sub *sh, struct osd_state *osd)
ctx->flush_on_seek = false;
}
static void uninit(struct sh_sub *sh)
static void uninit(struct sd *sd)
{
struct sd_ass_priv *ctx = sh->context;
struct sd_ass_priv *ctx = sd->priv;
ass_free_track(ctx->ass_track);
if (sd->ass_track != ctx->ass_track)
ass_free_track(ctx->ass_track);
talloc_free(ctx);
}
const struct sd_functions sd_ass = {
.probe = probe,
.name = "ass",
.accept_packets_in_advance = true,
.supports_format = supports_format,
.init = init,
.decode = decode,
.get_bitmaps = get_bitmaps,
.get_text = get_text,
.fix_events = fix_events,
.reset = reset,
.switch_off = reset,
.uninit = uninit,
};
static int sd_ass_track_destructor(void *ptr)
struct ass_track *sub_get_ass_track(struct dec_sub *sub)
{
uninit(ptr);
return 1;
}
struct sh_sub *sd_ass_create_from_track(struct ass_track *track,
bool vsfilter_aspect,
struct MPOpts *opts)
{
struct sh_sub *sh = talloc(NULL, struct sh_sub);
talloc_set_destructor(sh, sd_ass_track_destructor);
*sh = (struct sh_sub) {
.opts = opts,
.gsh = talloc_struct(sh, struct sh_stream, {
.codec = "ass",
}),
.sd_driver = &sd_ass,
.context = talloc_struct(sh, struct sd_ass_priv, {
.ass_track = track,
.vsfilter_aspect = vsfilter_aspect,
}),
.initialized = true,
};
return sh;
}
struct ass_track *sub_get_ass_track(struct osd_state *osd)
{
struct sh_sub *sh = osd ? osd->sh_sub : NULL;
if (sh && sh->sd_driver == &sd_ass && sh->context) {
struct sd_ass_priv *ctx = sh->context;
struct sd *sd = sub_get_last_sd(sub);
if (sd && sd->driver == &sd_ass && sd->priv) {
struct sd_ass_priv *ctx = sd->priv;
return ctx->ass_track;
}
return NULL;

View File

@ -41,15 +41,20 @@ struct sd_lavc_priv {
double endpts;
};
static bool probe(struct sh_sub *sh)
static bool supports_format(const char *format)
{
enum AVCodecID cid = mp_codec_to_av_codec_id(sh->gsh->codec);
enum AVCodecID cid = mp_codec_to_av_codec_id(format);
// Supported codecs must be known to decode to paletted bitmaps
switch (cid) {
case AV_CODEC_ID_DVB_SUBTITLE:
case AV_CODEC_ID_HDMV_PGS_SUBTITLE:
case AV_CODEC_ID_XSUB:
// lavc dvdsubdec doesn't read color/resolution on Libav 9.1 and below,
// so fall back to sd_spu in this case. Never use sd_spu with new ffmpeg;
// spudec can't handle ffmpeg .idx demuxing (added to lavc in 54.79.100).
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 40, 0)
case AV_CODEC_ID_DVD_SUBTITLE:
#endif
return true;
default:
return false;
@ -76,12 +81,10 @@ static void guess_resolution(enum AVCodecID type, int *w, int *h)
}
}
static int init(struct sh_sub *sh, struct osd_state *osd)
static int init(struct sd *sd)
{
if (sh->initialized)
return 0;
struct sd_lavc_priv *priv = talloc_zero(NULL, struct sd_lavc_priv);
enum AVCodecID cid = mp_codec_to_av_codec_id(sh->gsh->codec);
enum AVCodecID cid = mp_codec_to_av_codec_id(sd->codec);
AVCodecContext *ctx = NULL;
AVCodec *sub_codec = avcodec_find_decoder(cid);
if (!sub_codec)
@ -89,12 +92,12 @@ static int init(struct sh_sub *sh, struct osd_state *osd)
ctx = avcodec_alloc_context3(sub_codec);
if (!ctx)
goto error;
ctx->extradata_size = sh->extradata_len;
ctx->extradata = sh->extradata;
ctx->extradata_size = sd->extradata_len;
ctx->extradata = sd->extradata;
if (avcodec_open2(ctx, sub_codec, NULL) < 0)
goto error;
priv->avctx = ctx;
sh->context = priv;
sd->priv = priv;
return 0;
error:
@ -121,18 +124,19 @@ static void clear(struct sd_lavc_priv *priv)
priv->have_sub = false;
}
static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
int data_len, double pts, double duration)
static void decode(struct sd *sd, struct demux_packet *packet)
{
struct sd_lavc_priv *priv = sh->context;
struct sd_lavc_priv *priv = sd->priv;
AVCodecContext *ctx = priv->avctx;
double pts = packet->pts;
double duration = packet->duration;
AVSubtitle sub;
AVPacket pkt;
clear(priv);
av_init_packet(&pkt);
pkt.data = data;
pkt.size = data_len;
pkt.data = packet->buffer;
pkt.size = packet->len;
pkt.pts = pts * 1000;
if (duration >= 0)
pkt.convergence_duration = duration * 1000;
@ -184,11 +188,10 @@ static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
}
}
static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
struct mp_osd_res d, double pts,
static void get_bitmaps(struct sd *sd, struct mp_osd_res d, double pts,
struct sub_bitmaps *res)
{
struct sd_lavc_priv *priv = sh->context;
struct sd_lavc_priv *priv = sd->priv;
if (priv->pts != MP_NOPTS_VALUE && pts < priv->pts)
return;
@ -220,9 +223,9 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
res->scaled = xscale != 1 || yscale != 1;
}
static void reset(struct sh_sub *sh, struct osd_state *osd)
static void reset(struct sd *sd)
{
struct sd_lavc_priv *priv = sh->context;
struct sd_lavc_priv *priv = sd->priv;
if (priv->pts == MP_NOPTS_VALUE)
clear(priv);
@ -230,9 +233,9 @@ static void reset(struct sh_sub *sh, struct osd_state *osd)
avcodec_flush_buffers(priv->avctx);
}
static void uninit(struct sh_sub *sh)
static void uninit(struct sd *sd)
{
struct sd_lavc_priv *priv = sh->context;
struct sd_lavc_priv *priv = sd->priv;
clear(priv);
avcodec_close(priv->avctx);
@ -241,11 +244,11 @@ static void uninit(struct sh_sub *sh)
}
const struct sd_functions sd_lavc = {
.probe = probe,
.name = "lavc",
.supports_format = supports_format,
.init = init,
.decode = decode,
.get_bitmaps = get_bitmaps,
.reset = reset,
.switch_off = reset,
.uninit = uninit,
};

165
sub/sd_lavc_conv.c Normal file
View File

@ -0,0 +1,165 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <assert.h>
#include <libavcodec/avcodec.h>
#include <libavutil/intreadwrite.h>
#include <libavutil/common.h>
#include "config.h"
#include "talloc.h"
#include "core/mp_msg.h"
#include "core/av_common.h"
#include "core/bstr.h"
#include "sd.h"
struct sd_lavc_priv {
AVCodecContext *avctx;
};
static bool supports_format(const char *format)
{
enum AVCodecID cid = mp_codec_to_av_codec_id(format);
const AVCodecDescriptor *desc = avcodec_descriptor_get(cid);
if (!desc)
return false;
#if HAVE_AV_CODEC_PROP_TEXT_SUB
// These are documented to support AVSubtitleRect->ass.
return desc->props & AV_CODEC_PROP_TEXT_SUB;
#else
const char *whitelist[] =
{"text", "ass", "ssa", "mov_text", "srt", "subrip", "microdvd", "mpl2",
"jacosub", "pjs", "sami", "realtext", "subviewer", "subviewer1",
"vplayer", "webvtt", 0};
for (int n = 0; whitelist[n]; n++) {
if (strcmp(format, whitelist[n]) == 0)
return true;
}
return false;
#endif
}
// Disable style definitions generated by the libavcodec converter.
// We always want the user defined style instead.
static void disable_styles(bstr header)
{
while (header.len) {
int n = bstr_find(header, bstr0("\nStyle: "));
if (n < 0)
break;
header.start[n + 1] = '#'; // turn into a comment
header = bstr_cut(header, 2);
}
}
static int init(struct sd *sd)
{
struct sd_lavc_priv *priv = talloc_zero(NULL, struct sd_lavc_priv);
AVCodecContext *avctx = NULL;
AVCodec *codec = avcodec_find_decoder(mp_codec_to_av_codec_id(sd->codec));
if (!codec)
goto error;
avctx = avcodec_alloc_context3(codec);
if (!avctx)
goto error;
avctx->extradata_size = sd->extradata_len;
avctx->extradata = sd->extradata;
if (avcodec_open2(avctx, codec, NULL) < 0)
goto error;
// Documented as "set by libavcodec", but there is no other way
avctx->time_base = (AVRational) {1, 1000};
priv->avctx = avctx;
sd->priv = priv;
sd->output_codec = "ass";
sd->output_extradata = avctx->subtitle_header;
sd->output_extradata_len = avctx->subtitle_header_size;
if (sd->output_extradata) {
sd->output_extradata = talloc_memdup(sd, sd->output_extradata,
sd->output_extradata_len);
disable_styles((bstr){sd->output_extradata, sd->output_extradata_len});
}
return 0;
error:
mp_msg(MSGT_SUBREADER, MSGL_ERR,
"Could not open libavcodec subtitle converter\n");
av_free(avctx);
talloc_free(priv);
return -1;
}
static void decode(struct sd *sd, struct demux_packet *packet)
{
struct sd_lavc_priv *priv = sd->priv;
AVCodecContext *avctx = priv->avctx;
double ts = av_q2d(av_inv_q(avctx->time_base));
AVSubtitle sub = {0};
AVPacket pkt;
int ret, got_sub;
mp_set_av_packet(&pkt, packet);
pkt.pts = packet->pts == MP_NOPTS_VALUE ? AV_NOPTS_VALUE : packet->pts * ts;
pkt.duration = packet->duration * ts;
ret = avcodec_decode_subtitle2(avctx, &sub, &got_sub, &pkt);
if (ret < 0) {
mp_msg(MSGT_OSD, MSGL_ERR, "Error decoding subtitle\n");
} else if (got_sub) {
for (int i = 0; i < sub.num_rects; i++) {
char *ass_line = sub.rects[i]->ass;
if (!ass_line)
break;
// This might contain embedded timestamps, using the "old" ffmpeg
// ASS packet format, in which case pts/duration might be ignored
// at a later point.
sd_conv_add_packet(sd, ass_line, strlen(ass_line),
packet->pts, packet->duration);
}
}
avsubtitle_free(&sub);
}
static void reset(struct sd *sd)
{
struct sd_lavc_priv *priv = sd->priv;
avcodec_flush_buffers(priv->avctx);
sd_conv_def_reset(sd);
}
static void uninit(struct sd *sd)
{
struct sd_lavc_priv *priv = sd->priv;
avcodec_close(priv->avctx);
av_free(priv->avctx);
talloc_free(priv);
}
const struct sd_functions sd_lavc_conv = {
.name = "lavc_conv",
.supports_format = supports_format,
.init = init,
.decode = decode,
.get_converted = sd_conv_def_get_converted,
.reset = reset,
.uninit = uninit,
};

346
sub/sd_microdvd.c Normal file
View File

@ -0,0 +1,346 @@
/*
* Subtitles converter to SSA/ASS in order to allow special formatting
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
#include <ctype.h>
#include <libavutil/common.h>
#include "core/mp_msg.h"
#include "core/bstr.h"
#include "sd.h"
struct line {
char *buf;
int bufsize;
int len;
};
#ifdef __GNUC__
static void append_text(struct line *dst, char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
#endif
static void append_text(struct line *dst, char *fmt, ...)
{
va_list va;
va_start(va, fmt);
int ret = vsnprintf(dst->buf + dst->len, dst->bufsize - dst->len, fmt, va);
if (ret < 0)
goto out;
dst->len += ret;
if (dst->len > dst->bufsize)
dst->len = dst->bufsize;
out:
va_end(va);
}
static int indexof(const char *s, int c)
{
char *f = strchr(s, c);
return f ? (f - s) : -1;
}
/*
* MicroDVD
*
* Based on the specifications found here:
* https://trac.videolan.org/vlc/ticket/1825#comment:6
*/
struct microdvd_tag {
char key;
int persistent;
uint32_t data1;
uint32_t data2;
struct bstr data_string;
};
#define MICRODVD_PERSISTENT_OFF 0
#define MICRODVD_PERSISTENT_ON 1
#define MICRODVD_PERSISTENT_OPENED 2
// Color, Font, Size, cHarset, stYle, Position, cOordinate
#define MICRODVD_TAGS "cfshyYpo"
static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag)
{
int tag_index = indexof(MICRODVD_TAGS, tag.key);
if (tag_index < 0)
return;
memcpy(&tags[tag_index], &tag, sizeof(tag));
}
// italic, bold, underline, strike-through
#define MICRODVD_STYLES "ibus"
static char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
{
while (*s == '{') {
char *start = s;
char tag_char = *(s + 1);
struct microdvd_tag tag = {0};
if (!tag_char || *(s + 2) != ':')
break;
s += 3;
switch (tag_char) {
/* Style */
case 'Y':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 'y':
while (*s && *s != '}') {
int style_index = indexof(MICRODVD_STYLES, *s);
if (style_index >= 0)
tag.data1 |= (1 << style_index);
s++;
}
if (*s != '}')
break;
/* We must distinguish persistent and non-persistent styles
* to handle this kind of style tags: {y:ib}{Y:us} */
tag.key = tag_char;
break;
/* Color */
case 'C':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 'c':
tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
if (*s != '}')
break;
tag.key = 'c';
break;
/* Font name */
case 'F':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 'f':
{
int len = indexof(s, '}');
if (len < 0)
break;
tag.data_string.start = s;
tag.data_string.len = len;
s += len;
tag.key = 'f';
break;
}
/* Font size */
case 'S':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 's':
tag.data1 = strtol(s, &s, 10);
if (*s != '}')
break;
tag.key = 's';
break;
/* Charset */
case 'H':
{
//TODO: not yet handled, just parsed.
int len = indexof(s, '}');
if (len < 0)
break;
tag.data_string.start = s;
tag.data_string.len = len;
s += len;
tag.key = 'h';
break;
}
/* Position */
case 'P':
tag.persistent = MICRODVD_PERSISTENT_ON;
tag.data1 = (*s++ == '1');
if (*s != '}')
break;
tag.key = 'p';
break;
/* Coordinates */
case 'o':
tag.persistent = MICRODVD_PERSISTENT_ON;
tag.data1 = strtol(s, &s, 10);
if (*s != ',')
break;
s++;
tag.data2 = strtol(s, &s, 10);
if (*s != '}')
break;
tag.key = 'o';
break;
default: /* Unknown tag, we consider it to be text */
break;
}
if (tag.key == 0)
return start;
microdvd_set_tag(tags, tag);
s++;
}
return s;
}
static void microdvd_open_tags(struct line *new_line, struct microdvd_tag *tags)
{
for (int i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
continue;
switch (tags[i].key) {
case 'Y':
case 'y':
for (int sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
if (tags[i].data1 & (1 << sidx))
append_text(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
break;
case 'c':
append_text(new_line, "{\\c&H%06X&}", tags[i].data1);
break;
case 'f':
append_text(new_line, "{\\fn%.*s}", BSTR_P(tags[i].data_string));
break;
case 's':
append_text(new_line, "{\\fs%d}", tags[i].data1);
break;
case 'p':
if (tags[i].data1 == 0)
append_text(new_line, "{\\an8}");
break;
case 'o':
append_text(new_line, "{\\pos(%d,%d)}",
tags[i].data1, tags[i].data2);
break;
}
if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
}
}
static void microdvd_close_no_persistent_tags(struct line *new_line,
struct microdvd_tag *tags)
{
int i;
for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) {
if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
continue;
switch (tags[i].key) {
case 'y':
for (int sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
if (tags[i].data1 & (1 << sidx))
append_text(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
break;
case 'c':
append_text(new_line, "{\\c}");
break;
case 'f':
append_text(new_line, "{\\fn}");
break;
case 's':
append_text(new_line, "{\\fs}");
break;
}
tags[i].key = 0;
}
}
static void convert_microdvd(const char *orig, char *dest, int dest_buffer_size)
{
/* line is not const to avoid warnings with strtol, etc.
* orig content won't be changed */
char *line = (char *)orig;
struct line new_line = {
.buf = dest,
.bufsize = dest_buffer_size,
};
struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
while (*line) {
line = microdvd_load_tags(tags, line);
microdvd_open_tags(&new_line, tags);
while (*line && *line != '|')
new_line.buf[new_line.len++] = *line++;
if (*line == '|') {
microdvd_close_no_persistent_tags(&new_line, tags);
append_text(&new_line, "\\N");
line++;
}
}
new_line.buf[new_line.len] = 0;
}
static const char *microdvd_ass_extradata =
"[Script Info]\n"
"ScriptType: v4.00+\n"
"PlayResX: 384\n"
"PlayResY: 288\n";
static bool supports_format(const char *format)
{
return format && strcmp(format, "microdvd") == 0;
}
static int init(struct sd *sd)
{
sd->output_codec = "ass-text";
sd->output_extradata = (char *)microdvd_ass_extradata;
sd->output_extradata_len = strlen(sd->output_extradata);
return 0;
}
static void decode(struct sd *sd, struct demux_packet *packet)
{
char dest[SD_MAX_LINE_LEN];
// Assume input buffer is padded with 0
convert_microdvd(packet->buffer, dest, sizeof(dest));
sd_conv_add_packet(sd, dest, strlen(dest), packet->pts, packet->duration);
}
const struct sd_functions sd_microdvd = {
.name = "microdvd",
.supports_format = supports_format,
.init = init,
.decode = decode,
.get_converted = sd_conv_def_get_converted,
.reset = sd_conv_def_reset,
};

55
sub/sd_movtext.c Normal file
View File

@ -0,0 +1,55 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <assert.h>
#include <libavutil/intreadwrite.h>
#include <libavutil/common.h>
#include "sd.h"
static bool supports_format(const char *format)
{
return format && strcmp(format, "mov_text") == 0;
}
static int init(struct sd *sd)
{
sd->output_codec = "text";
return 0;
}
static void decode(struct sd *sd, struct demux_packet *packet)
{
unsigned char *data = packet->buffer;
int len = packet->len;
if (len < 2)
return;
len = FFMIN(len - 2, AV_RB16(data));
data += 2;
sd_conv_add_packet(sd, data, len, packet->pts, packet->duration);
}
const struct sd_functions sd_movtext = {
.name = "movtext",
.supports_format = supports_format,
.init = init,
.decode = decode,
.get_converted = sd_conv_def_get_converted,
.reset = sd_conv_def_reset,
};

102
sub/sd_spu.c Normal file
View File

@ -0,0 +1,102 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <assert.h>
#include "talloc.h"
#include "core/options.h"
#include "demux/stheader.h"
#include "sd.h"
#include "sub.h"
#include "spudec.h"
struct sd_spu_priv {
void *spudec;
};
static bool is_dvd_sub(const char *t)
{
return t && (strcmp(t, "dvd_subtitle") == 0 ||
strcmp(t, "dvd_subtitle_mpg") == 0);
}
static bool supports_format(const char *format)
{
return is_dvd_sub(format);
}
static int init(struct sd *sd)
{
void *spudec = spudec_new_scaled(sd->sub_video_w, sd->sub_video_h,
sd->extradata, sd->extradata_len);
if (!spudec)
return -1;
struct sd_spu_priv *priv = talloc_zero(NULL, struct sd_spu_priv);
priv->spudec = spudec;
sd->priv = priv;
return 0;
}
static void decode(struct sd *sd, struct demux_packet *packet)
{
struct sd_spu_priv *priv = sd->priv;
if (packet->pts < 0 || packet->len == 0)
return;
spudec_assemble(priv->spudec, packet->buffer, packet->len,
packet->pts * 90000);
}
static void get_bitmaps(struct sd *sd, struct mp_osd_res d, double pts,
struct sub_bitmaps *res)
{
struct MPOpts *opts = sd->opts;
struct sd_spu_priv *priv = sd->priv;
spudec_set_forced_subs_only(priv->spudec, opts->forced_subs_only);
spudec_heartbeat(priv->spudec, pts * 90000);
if (spudec_visible(priv->spudec))
spudec_get_indexed(priv->spudec, &d, res);
}
static void reset(struct sd *sd)
{
struct sd_spu_priv *priv = sd->priv;
spudec_reset(priv->spudec);
}
static void uninit(struct sd *sd)
{
struct sd_spu_priv *priv = sd->priv;
spudec_free(priv->spudec);
talloc_free(priv);
}
const struct sd_functions sd_spu = {
.name = "spu",
.supports_format = supports_format,
.init = init,
.decode = decode,
.get_bitmaps = get_bitmaps,
.reset = reset,
.uninit = uninit,
};

View File

@ -25,11 +25,11 @@
#include <stdarg.h>
#include <stdbool.h>
#include <ctype.h>
#include <libavutil/common.h>
#include "core/mp_msg.h"
#include "subassconvert.h"
#include "core/bstr.h"
#include "libavutil/common.h"
#include "sd.h"
struct line {
char *buf;
@ -60,13 +60,6 @@ static void append_text_n(struct line *dst, char *start, size_t length)
append_text(dst, "%.*s", (int)length, start);
}
static int indexof(const char *s, int c)
{
char *f = strchr(s, c);
return f ? (f - s) : -1;
}
/*
* SubRip
@ -280,7 +273,7 @@ static int read_attr(char **s, struct bstr *attr, struct bstr *val)
return 0;
}
void subassconvert_subrip(const char *orig, char *dest, int dest_buffer_size)
static void convert_subrip(const char *orig, char *dest, int dest_buffer_size)
{
/* line is not const to avoid warnings with strtol, etc.
* orig content won't be changed */
@ -444,251 +437,39 @@ void subassconvert_subrip(const char *orig, char *dest, int dest_buffer_size)
new_line.buf[new_line.len] = 0;
}
static const char *srt_ass_extradata =
"[Script Info]\n"
"ScriptType: v4.00+\n"
"PlayResX: 384\n"
"PlayResY: 288\n";
/*
* MicroDVD
*
* Based on the specifications found here:
* https://trac.videolan.org/vlc/ticket/1825#comment:6
*/
static bool supports_format(const char *format)
{
return format && (strcmp(format, "subrip") == 0 ||
strcmp(format, "text") == 0);
}
struct microdvd_tag {
char key;
int persistent;
uint32_t data1;
uint32_t data2;
struct bstr data_string;
static int init(struct sd *sd)
{
sd->output_codec = "ass-text";
sd->output_extradata = (char *)srt_ass_extradata;
sd->output_extradata_len = strlen(sd->output_extradata);
return 0;
}
static void decode(struct sd *sd, struct demux_packet *packet)
{
char dest[SD_MAX_LINE_LEN];
// Assume input buffer is padded with 0
convert_subrip(packet->buffer, dest, sizeof(dest));
sd_conv_add_packet(sd, dest, strlen(dest), packet->pts, packet->duration);
}
const struct sd_functions sd_srt = {
.name = "srt",
.supports_format = supports_format,
.init = init,
.decode = decode,
.get_converted = sd_conv_def_get_converted,
.reset = sd_conv_def_reset,
};
#define MICRODVD_PERSISTENT_OFF 0
#define MICRODVD_PERSISTENT_ON 1
#define MICRODVD_PERSISTENT_OPENED 2
// Color, Font, Size, cHarset, stYle, Position, cOordinate
#define MICRODVD_TAGS "cfshyYpo"
static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag)
{
int tag_index = indexof(MICRODVD_TAGS, tag.key);
if (tag_index < 0)
return;
memcpy(&tags[tag_index], &tag, sizeof(tag));
}
// italic, bold, underline, strike-through
#define MICRODVD_STYLES "ibus"
static char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
{
while (*s == '{') {
char *start = s;
char tag_char = *(s + 1);
struct microdvd_tag tag = {0};
if (!tag_char || *(s + 2) != ':')
break;
s += 3;
switch (tag_char) {
/* Style */
case 'Y':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 'y':
while (*s && *s != '}') {
int style_index = indexof(MICRODVD_STYLES, *s);
if (style_index >= 0)
tag.data1 |= (1 << style_index);
s++;
}
if (*s != '}')
break;
/* We must distinguish persistent and non-persistent styles
* to handle this kind of style tags: {y:ib}{Y:us} */
tag.key = tag_char;
break;
/* Color */
case 'C':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 'c':
tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
if (*s != '}')
break;
tag.key = 'c';
break;
/* Font name */
case 'F':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 'f':
{
int len = indexof(s, '}');
if (len < 0)
break;
tag.data_string.start = s;
tag.data_string.len = len;
s += len;
tag.key = 'f';
break;
}
/* Font size */
case 'S':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 's':
tag.data1 = strtol(s, &s, 10);
if (*s != '}')
break;
tag.key = 's';
break;
/* Charset */
case 'H':
{
//TODO: not yet handled, just parsed.
int len = indexof(s, '}');
if (len < 0)
break;
tag.data_string.start = s;
tag.data_string.len = len;
s += len;
tag.key = 'h';
break;
}
/* Position */
case 'P':
tag.persistent = MICRODVD_PERSISTENT_ON;
tag.data1 = (*s++ == '1');
if (*s != '}')
break;
tag.key = 'p';
break;
/* Coordinates */
case 'o':
tag.persistent = MICRODVD_PERSISTENT_ON;
tag.data1 = strtol(s, &s, 10);
if (*s != ',')
break;
s++;
tag.data2 = strtol(s, &s, 10);
if (*s != '}')
break;
tag.key = 'o';
break;
default: /* Unknown tag, we consider it to be text */
break;
}
if (tag.key == 0)
return start;
microdvd_set_tag(tags, tag);
s++;
}
return s;
}
static void microdvd_open_tags(struct line *new_line, struct microdvd_tag *tags)
{
for (int i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
continue;
switch (tags[i].key) {
case 'Y':
case 'y':
for (int sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
if (tags[i].data1 & (1 << sidx))
append_text(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
break;
case 'c':
append_text(new_line, "{\\c&H%06X&}", tags[i].data1);
break;
case 'f':
append_text(new_line, "{\\fn%.*s}", BSTR_P(tags[i].data_string));
break;
case 's':
append_text(new_line, "{\\fs%d}", tags[i].data1);
break;
case 'p':
if (tags[i].data1 == 0)
append_text(new_line, "{\\an8}");
break;
case 'o':
append_text(new_line, "{\\pos(%d,%d)}",
tags[i].data1, tags[i].data2);
break;
}
if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
}
}
static void microdvd_close_no_persistent_tags(struct line *new_line,
struct microdvd_tag *tags)
{
int i;
for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) {
if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
continue;
switch (tags[i].key) {
case 'y':
for (int sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
if (tags[i].data1 & (1 << sidx))
append_text(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
break;
case 'c':
append_text(new_line, "{\\c}");
break;
case 'f':
append_text(new_line, "{\\fn}");
break;
case 's':
append_text(new_line, "{\\fs}");
break;
}
tags[i].key = 0;
}
}
void subassconvert_microdvd(const char *orig, char *dest, int dest_buffer_size)
{
/* line is not const to avoid warnings with strtol, etc.
* orig content won't be changed */
char *line = (char *)orig;
struct line new_line = {
.buf = dest,
.bufsize = dest_buffer_size,
};
struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
while (*line) {
line = microdvd_load_tags(tags, line);
microdvd_open_tags(&new_line, tags);
while (*line && *line != '|')
new_line.buf[new_line.len++] = *line++;
if (*line == '|') {
microdvd_close_no_persistent_tags(&new_line, tags);
append_text(&new_line, "\\N");
line++;
}
}
new_line.buf[new_line.len] = 0;
}

View File

@ -575,6 +575,13 @@ void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pt
#endif
}
void spudec_set_changed(void *this)
{
spudec_handle_t *spu = this;
spu->spu_changed = 1;
}
void spudec_reset(void *this) // called after seek
{
spudec_handle_t *spu = this;
@ -583,6 +590,7 @@ void spudec_reset(void *this) // called after seek
spu->now_pts = 0;
spu->end_pts = 0;
spu->packet_size = spu->packet_offset = 0;
spudec_set_changed(spu);
}
void spudec_heartbeat(void *this, unsigned int pts100)
@ -611,7 +619,7 @@ void spudec_heartbeat(void *this, unsigned int pts100)
spudec_process_data(spu, packet);
}
spudec_free_packet(packet);
spu->spu_changed = 1;
spudec_set_changed(spu);
}
}
@ -621,15 +629,18 @@ int spudec_visible(void *this){
spu->now_pts < spu->end_pts &&
spu->height > 0);
// printf("spu visible: %d \n",ret);
if ((spu->forced_subs_only) && !(spu->is_forced_sub))
ret = 0;
return ret;
}
void spudec_set_forced_subs_only(void * const this, const unsigned int flag)
{
if(this){
((spudec_handle_t *)this)->forced_subs_only=flag;
mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU: Display only forced subs now %s\n", flag ? "enabled": "disabled");
}
spudec_handle_t *spu = this;
if (!!flag != !!spu->forced_subs_only) {
spu->forced_subs_only = !!flag;
spudec_set_changed(spu);
}
}
void spudec_get_indexed(void *this, struct mp_osd_res *dim,
@ -738,17 +749,14 @@ static void spudec_parse_extradata(spudec_handle_t *this,
free(buffer);
}
void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len)
void *spudec_new_scaled(unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len)
{
spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t));
if (this){
this->orig_frame_height = frame_height;
this->orig_frame_width = frame_width;
// set up palette:
if (palette)
memcpy(this->global_palette, palette, sizeof(this->global_palette));
else
this->auto_palette = 1;
this->auto_palette = 1;
if (extradata)
spudec_parse_extradata(this, extradata, extradata_len);
/* XXX Although the video frame is some size, the SPU frame is
@ -768,11 +776,6 @@ void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigne
return this;
}
void *spudec_new(unsigned int *palette)
{
return spudec_new_scaled(palette, 0, 0, NULL, 0);
}
void spudec_free(void *this)
{
spudec_handle_t *spu = this;

View File

@ -27,12 +27,12 @@ struct mp_osd_res;
void spudec_heartbeat(void *this, unsigned int pts100);
void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pts100);
void spudec_get_indexed(void *this, struct mp_osd_res *dim, struct sub_bitmaps *res);
void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len);
void *spudec_new(unsigned int *palette);
void *spudec_new_scaled(unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len);
void spudec_free(void *this);
void spudec_reset(void *this); // called after seek
int spudec_visible(void *this); // check if spu is visible
int spudec_changed(void *this);
void spudec_set_changed(void *this);
void spudec_set_forced_subs_only(void * const this, const unsigned int flag);
#endif /* MPLAYER_SPUDEC_H */

View File

@ -37,22 +37,10 @@
#include "dec_sub.h"
#include "img_convert.h"
#include "draw_bmp.h"
#include "spudec.h"
#include "subreader.h"
#include "video/mp_image.h"
#include "video/mp_image_pool.h"
int sub_pos=100;
int sub_visibility=1;
subtitle* vo_sub=NULL;
float sub_delay = 0;
float sub_fps = 0;
void *vo_spudec=NULL;
void *vo_vobsub=NULL;
static const struct osd_style_opts osd_style_opts_def = {
.font = "Sans",
.font_size = 45,
@ -86,8 +74,6 @@ const struct m_sub_options osd_style_conf = {
.defaults = &osd_style_opts_def,
};
static struct osd_state *global_osd;
static bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b)
{
return a.w == b.w && a.h == b.h && a.ml == b.ml && a.mt == b.mt
@ -103,6 +89,7 @@ struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib)
.opts = opts,
.ass_library = asslib,
.osd_text = talloc_strdup(osd, ""),
.sub_text = talloc_strdup(osd, ""),
.progbar_type = -1,
};
@ -115,12 +102,10 @@ struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib)
osd->objs[n] = obj;
}
osd->objs[OSDTYPE_SPU]->is_sub = true; // spudec.c
osd->objs[OSDTYPE_SUB]->is_sub = true; // dec_sub.c
osd->objs[OSDTYPE_SUBTITLE]->is_sub = true; // osd_libass.c
osd->objs[OSDTYPE_SUBTEXT]->is_sub = true; // osd_libass.c
osd_init_backend(osd);
global_osd = osd;
return osd;
}
@ -130,24 +115,29 @@ void osd_free(struct osd_state *osd)
return;
osd_destroy_backend(osd);
talloc_free(osd);
global_osd = NULL;
}
static bool set_text(void *talloc_ctx, char **var, const char *text)
{
if (!text)
text = "";
if (strcmp(*var, text) == 0)
return true;
talloc_free(*var);
*var = talloc_strdup(talloc_ctx, text);
return false;
}
void osd_set_text(struct osd_state *osd, const char *text)
{
if (!text)
text = "";
if (strcmp(osd->osd_text, text) == 0)
return;
talloc_free(osd->osd_text);
osd->osd_text = talloc_strdup(osd, text);
vo_osd_changed(OSDTYPE_OSD);
if (!set_text(osd, &osd->osd_text, text))
osd_changed(osd, OSDTYPE_OSD);
}
static bool spu_visible(struct osd_state *osd, struct osd_object *obj)
void osd_set_sub(struct osd_state *osd, const char *text)
{
struct MPOpts *opts = osd->opts;
return opts->sub_visibility && vo_spudec && spudec_visible(vo_spudec);
if (!set_text(osd, &osd->sub_text, text))
osd_changed(osd, OSDTYPE_SUBTEXT);
}
static void render_object(struct osd_state *osd, struct osd_object *obj,
@ -168,14 +158,13 @@ static void render_object(struct osd_state *osd, struct osd_object *obj,
obj->force_redraw = true;
obj->vo_res = res;
if (obj->type == OSDTYPE_SPU) {
if (spu_visible(osd, obj))
spudec_get_indexed(vo_spudec, &obj->vo_res, out_imgs);
} else if (obj->type == OSDTYPE_SUB) {
double sub_pts = video_pts;
if (sub_pts != MP_NOPTS_VALUE)
sub_pts += sub_delay - osd->sub_offset;
sub_get_bitmaps(osd, obj->vo_res, sub_pts, out_imgs);
if (obj->type == OSDTYPE_SUB) {
if (osd->render_bitmap_subs && osd->dec_sub) {
double sub_pts = video_pts;
if (sub_pts != MP_NOPTS_VALUE)
sub_pts -= osd->sub_offset;
sub_get_bitmaps(osd->dec_sub, obj->vo_res, sub_pts, out_imgs);
}
} else {
osd_object_get_bitmaps(osd, obj, out_imgs);
}
@ -303,9 +292,8 @@ void osd_draw_on_image_p(struct osd_state *osd, struct mp_osd_res res,
&draw_on_image, &closure);
}
void vo_osd_changed(int new_value)
void osd_changed(struct osd_state *osd, int new_value)
{
struct osd_state *osd = global_osd;
for (int n = 0; n < MAX_OSD_PARTS; n++) {
if (osd->objs[n]->type == new_value)
osd->objs[n]->force_redraw = true;
@ -316,5 +304,5 @@ void vo_osd_changed(int new_value)
void osd_changed_all(struct osd_state *osd)
{
for (int n = 0; n < MAX_OSD_PARTS; n++)
vo_osd_changed(n);
osd_changed(osd, n);
}

View File

@ -85,8 +85,7 @@ struct mp_osd_res {
enum mp_osdtype {
OSDTYPE_SUB,
OSDTYPE_SUBTITLE,
OSDTYPE_SPU,
OSDTYPE_SUBTEXT,
OSDTYPE_PROGBAR,
OSDTYPE_OSD,
@ -121,23 +120,25 @@ struct osd_state {
struct ass_library *ass_library;
struct ass_renderer *ass_renderer;
struct sh_sub *sh_sub;
double sub_offset;
double vo_pts;
bool render_subs_in_filter;
bool render_bitmap_subs;
bool want_redraw;
// OSDTYPE_OSD
char *osd_text;
// OSDTYPE_SUBTEXT
char *sub_text;
// OSDTYPE_PROGBAR
int progbar_type; // <0: disabled, 1-255: symbol, else: no symbol
float progbar_value; // range 0.0-1.0
float *progbar_stops; // used for chapter indicators (0.0-1.0 each)
int progbar_num_stops;
int switch_sub_id;
// OSDTYPE_SUB
struct dec_sub *dec_sub;
struct MPOpts *opts;
@ -149,11 +150,6 @@ struct osd_state {
struct ass_library *osd_ass_library;
};
extern struct subtitle* vo_sub;
extern void* vo_spudec;
extern void* vo_vobsub;
// Start of OSD symbols in osd_font.pfb
#define OSD_CODEPOINTS 0xE000
@ -197,16 +193,10 @@ struct osd_style_opts {
extern const struct m_sub_options osd_style_conf;
extern char *sub_cp;
extern int sub_pos;
extern float sub_delay;
extern float sub_fps;
struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib);
void osd_set_text(struct osd_state *osd, const char *text);
void vo_osd_changed(int new_value);
void osd_set_sub(struct osd_state *osd, const char *text);
void osd_changed(struct osd_state *osd, int new_value);
void osd_changed_all(struct osd_state *osd);
void osd_free(struct osd_state *osd);

View File

@ -1,27 +0,0 @@
/*
* Header for subtitles converter to SSA/ASS
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_SUBASSCONVERT_H
#define MPLAYER_SUBASSCONVERT_H
void subassconvert_subrip(const char *orig, char *dest, int dest_buffer_size);
void subassconvert_microdvd(const char *orig, char *dest, int dest_buffer_size);
#endif

View File

@ -32,7 +32,6 @@
#include "core/mp_msg.h"
#include "subreader.h"
#include "core/mp_common.h"
#include "subassconvert.h"
#include "core/options.h"
#include "stream/stream.h"
#include "libavutil/common.h"
@ -48,42 +47,33 @@
#include <iconv.h>
#endif
char *sub_cp=NULL;
int suboverlap_enabled = 1;
// Parameter struct for the format-specific readline functions
struct readline_args {
int utf16;
struct MPOpts *opts;
// subtitle reader state used by some formats
float mpsub_multiplier;
float mpsub_position;
int sub_slacktime;
/*
Some subtitling formats, namely AQT and Subrip09, define the end of a
subtitle as the beginning of the following. Since currently we read one
subtitle at time, for these format we keep two global *subtitle,
previous_aqt_sub and previous_subrip09_sub, pointing to previous subtitle,
so we can change its end when we read current subtitle starting time.
We use a single global unsigned long,
previous_sub_end, for both (and even future) formats, to store the end of
the previous sub: it is initialized to 0 in sub_read_file and eventually
modified by sub_read_aqt_line or sub_read_subrip09_line.
*/
unsigned long previous_sub_end;
};
/* Maximal length of line of a subtitle */
#define LINE_LEN 1000
static float mpsub_position=0;
static float mpsub_multiplier=1.;
static int sub_slacktime = 20000; //20 sec
int sub_no_text_pp=0; // 1 => do not apply text post-processing
// like {\...} elimination in SSA format.
int sub_match_fuzziness=0; // level of sub name matching fuzziness
/* Use the SUB_* constant defined in the header file */
int sub_format=SUB_INVALID;
/*
Some subtitling formats, namely AQT and Subrip09, define the end of a
subtitle as the beginning of the following. Since currently we read one
subtitle at time, for these format we keep two global *subtitle,
previous_aqt_sub and previous_subrip09_sub, pointing to previous subtitle,
so we can change its end when we read current subtitle starting time.
We use a single global unsigned long,
previous_sub_end, for both (and even future) formats, to store the end of
the previous sub: it is initialized to 0 in sub_read_file and eventually
modified by sub_read_aqt_line or sub_read_subrip09_line.
*/
unsigned long previous_sub_end;
static int eol(char p) {
return p=='\r' || p=='\n' || p=='\0';
@ -145,7 +135,7 @@ static subtitle *sub_read_line_sami(stream_t* st, subtitle *current,
case 0: /* find "START=" or "Slacktime:" */
slacktime_s = stristr (s, "Slacktime:");
if (slacktime_s)
sub_slacktime = strtol (slacktime_s+10, NULL, 0) / 10;
args->sub_slacktime = strtol (slacktime_s+10, NULL, 0) / 10;
s = stristr (s, "Start=");
if (s) {
@ -181,7 +171,7 @@ static subtitle *sub_read_line_sami(stream_t* st, subtitle *current,
sami_add_line(current, text, &p);
s += 4;
}
else if ((*s == '{') && !sub_no_text_pp) { state = 5; ++s; continue; }
else if ((*s == '{') && !args->opts->sub_no_text_pp) { state = 5; ++s; continue; }
else if (*s == '<') { state = 4; }
else if (!strncasecmp (s, "&nbsp;", 6)) { *p++ = ' '; s += 6; }
else if (*s == '\t') { *p++ = ' '; s++; }
@ -207,7 +197,7 @@ static subtitle *sub_read_line_sami(stream_t* st, subtitle *current,
if (s) { s++; state = 3; continue; }
break;
case 5: /* get rid of {...} text, but read the alignment code */
if ((*s == '\\') && (*(s + 1) == 'a') && !sub_no_text_pp) {
if ((*s == '\\') && (*(s + 1) == 'a') && !args->opts->sub_no_text_pp) {
if (stristr(s, "\\a1") != NULL) {
current->alignment = SUB_ALIGNMENT_BOTTOMLEFT;
s = s + 3;
@ -256,7 +246,7 @@ static subtitle *sub_read_line_sami(stream_t* st, subtitle *current,
// For the last subtitle
if (current->end <= 0) {
current->end = current->start + sub_slacktime;
current->end = current->start + args->sub_slacktime;
sami_add_line(current, text, &p);
}
@ -308,7 +298,6 @@ static subtitle *sub_read_line_microdvd(stream_t *st,subtitle *current,
int utf16 = args->utf16;
char line[LINE_LEN+1];
char line2[LINE_LEN+1];
char *p;
do {
if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL;
@ -319,13 +308,7 @@ static subtitle *sub_read_line_microdvd(stream_t *st,subtitle *current,
"{%ld}{%ld}%[^\r\n]",
&(current->start), &(current->end), line2) < 3));
if (args->opts->ass_enabled) {
subassconvert_microdvd(line2, line, LINE_LEN + 1);
p = line;
} else
p = line2;
return set_multiline_text(current, p, 0);
return set_multiline_text(current, line2, 0);
}
static subtitle *sub_read_line_mpl2(stream_t *st,subtitle *current,
@ -379,8 +362,8 @@ static subtitle *sub_read_line_subrip(stream_t* st, subtitle *current,
return current;
}
static subtitle *sub_ass_read_line_subviewer(stream_t *st, subtitle *current,
struct readline_args *args)
static subtitle *sub_read_line_subviewer(stream_t *st, subtitle *current,
struct readline_args *args)
{
int utf16 = args->utf16;
int a1, a2, a3, a4, b1, b2, b3, b4, j = 0;
@ -426,74 +409,14 @@ static subtitle *sub_ass_read_line_subviewer(stream_t *st, subtitle *current,
j += len;
}
/* Use the ASS/SSA converter to transform the whole lines */
if (full_line[0]) {
char converted_line[LINE_LEN + 1];
subassconvert_subrip(full_line, converted_line, LINE_LEN + 1);
current->text[0] = strdup(converted_line);
current->text[0] = strdup(full_line);
current->lines = 1;
}
}
return current;
}
static subtitle *sub_read_line_subviewer(stream_t *st,subtitle *current,
struct readline_args *args)
{
int utf16 = args->utf16;
char line[LINE_LEN+1];
int a1,a2,a3,a4,b1,b2,b3,b4;
char *p=NULL;
int i,len;
if (args->opts->ass_enabled)
return sub_ass_read_line_subviewer(st, current, args);
while (!current->text[0]) {
if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL;
if ((len=sscanf (line, "%d:%d:%d%*1[,.:]%d --> %d:%d:%d%*1[,.:]%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4)) < 8)
continue;
current->start = a1*360000+a2*6000+a3*100+a4/10;
current->end = b1*360000+b2*6000+b3*100+b4/10;
for (i=0; i<SUB_MAX_TEXT;) {
int blank = 1;
if (!stream_read_line (st, line, LINE_LEN, utf16)) break;
len=0;
for (p=line; *p!='\n' && *p!='\r' && *p; p++,len++)
if (*p != ' ' && *p != '\t')
blank = 0;
if (len && !blank) {
int j=0,skip=0;
char *curptr=current->text[i]=malloc (len+1);
if (!current->text[i]) return ERR;
//strncpy (current->text[i], line, len); current->text[i][len]='\0';
for(; j<len; j++) {
/* let's filter html tags ::atmos */
if(line[j]=='>') {
skip=0;
continue;
}
if(line[j]=='<') {
skip=1;
continue;
}
if(skip) {
continue;
}
*curptr=line[j];
curptr++;
}
*curptr='\0';
i++;
} else {
break;
}
}
current->lines=i;
}
return current;
}
static subtitle *sub_read_line_subviewer2(stream_t *st,subtitle *current,
struct readline_args *args)
{
@ -684,20 +607,6 @@ static subtitle *sub_read_line_ssa(stream_t *st,subtitle *current,
return current;
}
static void sub_pp_ssa(subtitle *sub)
{
for (int i = 0; i < sub->lines; i++) {
char *s, *d;
s = d = sub->text[i];
while (1) {
while (*s == '{')
while (*s && *s++ != '}');
if (!(*d++ = *s++))
break;
}
}
}
/*
* PJS subtitles reader.
* That's the "Phoenix Japanimation Society" format.
@ -761,10 +670,10 @@ static subtitle *sub_read_line_mpsub(stream_t *st, subtitle *current,
if (!stream_read_line(st, line, LINE_LEN, utf16)) return NULL;
} while (sscanf (line, "%f %f", &a, &b) !=2);
mpsub_position += a*mpsub_multiplier;
current->start=(int) mpsub_position;
mpsub_position += b*mpsub_multiplier;
current->end=(int) mpsub_position;
args->mpsub_position += a*args->mpsub_multiplier;
current->start=(int) args->mpsub_position;
args->mpsub_position += b*args->mpsub_multiplier;
current->end=(int) args->mpsub_position;
while (num < SUB_MAX_TEXT) {
if (!stream_read_line (st, line, LINE_LEN, utf16)) {
@ -805,8 +714,8 @@ retry:
break;
}
if (!previous_sub_end)
previous_sub_end = (current->start) ? current->start - 1 : 0;
if (!args->previous_sub_end)
args->previous_sub_end = (current->start) ? current->start - 1 : 0;
if (!stream_read_line (st, line, LINE_LEN, utf16))
return NULL;
@ -846,8 +755,8 @@ retry:
current->start = a1*360000+a2*6000+a3*100;
if (!previous_sub_end)
previous_sub_end = (current->start) ? current->start - 1 : 0;
if (!args->previous_sub_end)
args->previous_sub_end = (current->start) ? current->start - 1 : 0;
if (!stream_read_line (st, line, LINE_LEN, utf16))
return NULL;
@ -1109,14 +1018,12 @@ static int sub_autodetect (stream_t* st, int *uses_time, int utf16) {
return SUB_INVALID; // too many bad lines
}
extern float sub_delay;
extern float sub_fps;
#ifdef CONFIG_ICONV
static iconv_t icdsc = (iconv_t)(-1);
static const char* guess_cp(stream_t *st, const char *preferred_language, const char *fallback);
void subcp_open (stream_t *st)
static iconv_t subcp_open (stream_t *st, const char *sub_cp)
{
iconv_t icdsc = (iconv_t)(-1);
char *tocp = "UTF-8";
if (sub_cp){
@ -1139,18 +1046,18 @@ void subcp_open (stream_t *st)
} else
mp_msg(MSGT_SUBREADER,MSGL_ERR,"SUB: error opening iconv descriptor.\n");
}
return icdsc;
}
void subcp_close (void)
static void subcp_close (iconv_t icdsc)
{
if (icdsc != (iconv_t)(-1)){
(void) iconv_close (icdsc);
icdsc = (iconv_t)(-1);
mp_msg(MSGT_SUBREADER,MSGL_V,"SUB: closed iconv descriptor.\n");
}
}
subtitle* subcp_recode (subtitle *sub)
static subtitle* subcp_recode (iconv_t icdsc, subtitle *sub)
{
int l=sub->lines;
size_t ileft, oleft;
@ -1184,7 +1091,8 @@ subtitle* subcp_recode (subtitle *sub)
}
#endif
static void adjust_subs_time(subtitle* sub, float subtime, float fps, int block,
static void adjust_subs_time(subtitle* sub, float subtime, float fps,
float sub_fps, int block,
int sub_num, int sub_uses_time) {
int n,m;
subtitle* nextsub;
@ -1248,10 +1156,11 @@ struct subreader {
struct readline_args *args);
void (*post)(subtitle *dest);
const char *name;
const char *codec_name;
};
#ifdef CONFIG_ENCA
const char* guess_buffer_cp(unsigned char* buffer, int buflen, const char *preferred_language, const char *fallback)
static const char* guess_buffer_cp(unsigned char* buffer, int buflen, const char *preferred_language, const char *fallback)
{
const char **languages;
size_t langcnt;
@ -1291,7 +1200,7 @@ const char* guess_buffer_cp(unsigned char* buffer, int buflen, const char *prefe
}
#define MAX_GUESS_BUFFER_SIZE (256*1024)
const char* guess_cp(stream_t *st, const char *preferred_language, const char *fallback)
static const char* guess_cp(stream_t *st, const char *preferred_language, const char *fallback)
{
size_t buflen;
unsigned char *buffer;
@ -1323,13 +1232,13 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
int uses_time = 0, sub_num = 0, sub_errs = 0;
static const struct subreader sr[]=
{
{ sub_read_line_microdvd, NULL, "microdvd" },
{ sub_read_line_microdvd, NULL, "microdvd", "microdvd" },
{ sub_read_line_subrip, NULL, "subviewer" },
{ sub_read_line_subviewer, NULL, "subrip" },
{ sub_read_line_subviewer, NULL, "subrip", "subrip" },
{ sub_read_line_sami, NULL, "sami" },
{ sub_read_line_vplayer, NULL, "vplayer" },
{ sub_read_line_rt, NULL, "rt" },
{ sub_read_line_ssa, sub_pp_ssa, "ssa" },
{ sub_read_line_ssa, NULL, "ssa", "ass-text" },
{ sub_read_line_pjs, NULL, "pjs" },
{ sub_read_line_mpsub, NULL, "mpsub" },
{ sub_read_line_aqt, NULL, "aqt" },
@ -1343,7 +1252,7 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
if(filename==NULL) return NULL; //qnx segfault
fd=open_stream (filename, NULL, NULL); if (!fd) return NULL;
sub_format = SUB_INVALID;
int sub_format = SUB_INVALID;
for (utf16 = 0; sub_format == SUB_INVALID && utf16 < 3; utf16++) {
sub_format=sub_autodetect (fd, &uses_time, utf16);
stream_reset(fd);
@ -1351,7 +1260,10 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
}
utf16--;
mpsub_multiplier = (uses_time ? 100.0 : 1.0);
struct readline_args args = {utf16, opts};
args.sub_slacktime = 20000; //20 sec
args.mpsub_multiplier = (uses_time ? 100.0 : 1.0);
if (sub_format==SUB_INVALID) {
mp_msg(MSGT_SUBREADER,MSGL_WARN,"SUB: Could not determine file format\n");
free_stream(fd);
@ -1361,6 +1273,7 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
mp_msg(MSGT_SUBREADER, MSGL_V, "SUB: Detected subtitle file format: %s\n", srp->name);
#ifdef CONFIG_ICONV
iconv_t icdsc = (iconv_t)(-1);
{
int l,k;
k = -1;
@ -1371,7 +1284,7 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
break;
}
}
if (k<0) subcp_open(fd);
if (k<0) icdsc = subcp_open(fd, opts->sub_cp);
}
#endif
@ -1384,22 +1297,22 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
sub = malloc(sizeof(subtitle));
//This is to deal with those formats (AQT & Subrip) which define the end of a subtitle
//as the beginning of the following
previous_sub_end = 0;
args.previous_sub_end = 0;
while(1){
if(sub_num>=n_max){
n_max+=16;
first=realloc(first,n_max*sizeof(subtitle));
}
memset(sub, '\0', sizeof(subtitle));
sub=srp->read(fd, sub, &(struct readline_args){utf16, opts});
sub=srp->read(fd, sub, &args);
if(!sub) break; // EOF
#ifdef CONFIG_ICONV
if (sub!=ERR) sub=subcp_recode(sub);
if (sub!=ERR) sub=subcp_recode(icdsc, sub);
#endif
if ( sub == ERR )
{
#ifdef CONFIG_ICONV
subcp_close();
subcp_close(icdsc);
#endif
free(first);
free(alloced_sub);
@ -1407,7 +1320,7 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
return NULL;
}
// Apply any post processing that needs recoding first
if ((sub!=ERR) && !sub_no_text_pp && srp->post) srp->post(sub);
if ((sub!=ERR) && !args.opts->sub_no_text_pp && srp->post) srp->post(sub);
if(!sub_num || (first[sub_num - 1].start <= sub->start)){
first[sub_num].start = sub->start;
first[sub_num].end = sub->end;
@ -1416,9 +1329,9 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
for(i = 0; i < sub->lines; ++i){
first[sub_num].text[i] = sub->text[i];
}
if (previous_sub_end){
first[sub_num - 1].end = previous_sub_end;
previous_sub_end = 0;
if (args.previous_sub_end){
first[sub_num - 1].end = args.previous_sub_end;
args.previous_sub_end = 0;
}
} else {
for(j = sub_num - 1; j >= 0; --j){
@ -1437,10 +1350,10 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
for(i = 0; i < SUB_MAX_TEXT; ++i){
first[j].text[i] = sub->text[i];
}
if (previous_sub_end){
if (args.previous_sub_end){
first[j].end = first[j - 1].end;
first[j - 1].end = previous_sub_end;
previous_sub_end = 0;
first[j - 1].end = args.previous_sub_end;
args.previous_sub_end = 0;
}
break;
}
@ -1452,7 +1365,7 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
free_stream(fd);
#ifdef CONFIG_ICONV
subcp_close();
subcp_close(icdsc);
#endif
free(alloced_sub);
@ -1469,9 +1382,9 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
// the user didn't forced no-overlapsub and the format is Jacosub or Ssa.
// this is because usually overlapping subtitles are found in these formats,
// while in others they are probably result of bad timing
if ((suboverlap_enabled == 2) ||
((suboverlap_enabled) && ((sub_format == SUB_JACOSUB) || (sub_format == SUB_SSA)))) {
adjust_subs_time(first, 6.0, fps, 0, sub_num, uses_time);/*~6 secs AST*/
if ((opts->suboverlap_enabled == 2) ||
((opts->suboverlap_enabled) && ((sub_format == SUB_JACOSUB) || (sub_format == SUB_SSA)))) {
adjust_subs_time(first, 6.0, fps, opts->sub_fps, 0, sub_num, uses_time);/*~6 secs AST*/
// here we manage overlapping subtitles
sub_orig = sub_num;
n_first = sub_num;
@ -1678,18 +1591,19 @@ if ((suboverlap_enabled == 2) ||
return_sub = second;
} else { //if(suboverlap_enabled)
adjust_subs_time(first, 6.0, fps, 1, sub_num, uses_time);/*~6 secs AST*/
adjust_subs_time(first, 6.0, fps, opts->sub_fps, 1, sub_num, uses_time);/*~6 secs AST*/
return_sub = first;
}
if (return_sub == NULL) return NULL;
subt_data = talloc_zero(NULL, sub_data);
talloc_set_destructor(subt_data, sub_destroy);
subt_data->codec = srp->name;
subt_data->codec = srp->codec_name ? srp->codec_name : "text";
subt_data->filename = strdup(filename);
subt_data->sub_uses_time = uses_time;
subt_data->sub_num = sub_num;
subt_data->sub_errs = sub_errs;
subt_data->subtitles = return_sub;
subt_data->fallback_fps = fps;
return subt_data;
}
@ -1704,103 +1618,3 @@ static int sub_destroy(void *ptr)
free( subd->filename );
return 0;
}
#define MAX_SUBLINE 512
/**
* \brief parse text and append it to subtitle in sub
* \param sub subtitle struct to add text to
* \param txt text to parse
* \param len length of text in txt
* \param endpts pts at which this subtitle text should be removed again
*
* <> and {} are interpreted as comment delimiters, "\n", "\N", '\n', '\r'
* and '\0' are interpreted as newlines, duplicate, leading and trailing
* newlines are ignored.
*/
void sub_add_text(subtitle *sub, const char *txt, int len, double endpts) {
int comment = 0;
int double_newline = 1; // ignore newlines at the beginning
int i, pos;
char *buf;
if (sub->lines >= SUB_MAX_TEXT) return;
pos = 0;
buf = malloc(MAX_SUBLINE + 1);
sub->text[sub->lines] = buf;
sub->endpts[sub->lines] = endpts;
for (i = 0; i < len && pos < MAX_SUBLINE; i++) {
char c = txt[i];
if (c == '<') comment |= 1;
if (c == '{') comment |= 2;
if (comment) {
if (c == '}') comment &= ~2;
if (c == '>') comment &= ~1;
continue;
}
if (pos == MAX_SUBLINE - 1) {
i--;
c = 0;
}
if (c == '\\' && i + 1 < len) {
c = txt[++i];
if (c == 'n' || c == 'N') c = 0;
}
if (c == '\n' || c == '\r') c = 0;
if (c) {
double_newline = 0;
buf[pos++] = c;
} else if (!double_newline) {
if (sub->lines >= SUB_MAX_TEXT - 1) {
mp_msg(MSGT_VO, MSGL_WARN, "Too many subtitle lines\n");
break;
}
double_newline = 1;
buf[pos] = 0;
sub->lines++;
pos = 0;
buf = malloc(MAX_SUBLINE + 1);
sub->text[sub->lines] = buf;
sub->endpts[sub->lines] = endpts;
}
}
buf[pos] = 0;
if (sub->lines < SUB_MAX_TEXT &&
strlen(sub->text[sub->lines]))
sub->lines++;
if (sub->lines > 1 &&
strcmp(sub->text[sub->lines-1], sub->text[sub->lines-2]) == 0) {
// remove duplicate lines. These can happen with some
// "clever" ASS effects.
sub->lines--;
sub->endpts[sub->lines-1] =
FFMAX(sub->endpts[sub->lines-1],
sub->endpts[sub->lines]);
free(sub->text[sub->lines]);
}
}
/**
* \brief remove outdated subtitle lines.
* \param sub subtitle struct to modify
* \param pts current pts. All lines with endpts <= this will be removed.
* Use MP_NOPTS_VALUE to remove all lines
* \return 1 if sub was modified, 0 otherwise.
*/
int sub_clear_text(subtitle *sub, double pts) {
int i = 0;
int changed = 0;
while (i < sub->lines) {
double endpts = sub->endpts[i];
if (pts == MP_NOPTS_VALUE || (endpts != MP_NOPTS_VALUE && pts >= endpts)) {
int j;
free(sub->text[i]);
for (j = i + 1; j < sub->lines; j++) {
sub->text[j - 1] = sub->text[j];
sub->endpts[j - 1] = sub->endpts[j];
}
sub->lines--;
changed = 1;
} else
i++;
}
return changed;
}

View File

@ -24,10 +24,6 @@
#include "config.h"
extern int suboverlap_enabled;
extern int sub_no_text_pp; // disable text post-processing
extern int sub_match_fuzziness;
// subtitle formats
#define SUB_INVALID -1
#define SUB_MICRODVD 0
@ -45,9 +41,6 @@ extern int sub_match_fuzziness;
#define SUB_JACOSUB 12
#define SUB_MPL2 13
// One of the SUB_* constant above
extern int sub_format;
#define SUB_MAX_TEXT 12
#define SUB_ALIGNMENT_BOTTOMLEFT 1
#define SUB_ALIGNMENT_BOTTOMCENTER 2
@ -67,7 +60,6 @@ typedef struct subtitle {
unsigned long end;
char *text[SUB_MAX_TEXT];
double endpts[SUB_MAX_TEXT];
unsigned char alignment;
} subtitle;
@ -78,24 +70,10 @@ typedef struct sub_data {
int sub_uses_time;
int sub_num; // number of subtitle structs
int sub_errs;
double fallback_fps;
} sub_data;
struct MPOpts;
sub_data* sub_read_file (char *filename, float pts, struct MPOpts *opts);
subtitle* subcp_recode (subtitle *sub);
// enca_fd is the file enca uses to determine the codepage.
// setting to NULL disables enca.
struct stream;
void subcp_open (struct stream *st); /* for demux_ogg.c */
void subcp_close (void); /* for demux_ogg.c */
#ifdef CONFIG_ENCA
const char* guess_buffer_cp(unsigned char* buffer, int buflen, const char *preferred_language, const char *fallback);
const char* guess_cp(struct stream *st, const char *preferred_language, const char *fallback);
#endif
struct MPContext;
void find_sub(struct MPContext *mpctx, sub_data* subd,int key);
void step_sub(sub_data *subd, float pts, int movement);
void sub_add_text(subtitle *sub, const char *txt, int len, double endpts);
int sub_clear_text(subtitle *sub, double pts);
#endif /* MPLAYER_SUBREADER_H */

View File

@ -673,17 +673,8 @@ static int decode(struct sh_video *sh, struct demux_packet *packet,
else
avctx->skip_frame = ctx->skip_frame;
av_init_packet(&pkt);
pkt.data = packet ? packet->buffer : NULL;
pkt.size = packet ? packet->len : 0;
/* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info
* from demuxer. */
if (packet && packet->keyframe)
pkt.flags |= AV_PKT_FLAG_KEY;
if (packet && packet->avpacket) {
pkt.side_data = packet->avpacket->side_data;
pkt.side_data_elems = packet->avpacket->side_data_elems;
}
mp_set_av_packet(&pkt, packet);
// The avcodec opaque field stupidly supports only int64_t type
union pts { int64_t i; double d; };
avctx->reordered_opaque = (union pts){.d = *reordered_pts}.i;