mirror of
https://github.com/mpv-player/mpv
synced 2024-08-24 07:21:49 +02:00
sub: redo how -no-ass is handled
The -no-ass switch used to disable any use of libass for text subtitles. This is not really the case anymore, because libass is now always involved when rendering text. The only remaining use of -no-ass is disabling styling or showing subtitles on the terminal. On the other hand, the old subtitle rendering path is a big reason why the subtitle code is still a big mess with an awful number of obscure special cases. In order to simplify it, remove the old subtitle rendering code, and always go through sd_ass.c. Basically, we use ASS_Track as central data structure for storing text subtitles instead of struct sub_data. This also makes libass mandatory for all text subs, even if they are printed to the terminal in -no-video mode. (We could add something like sd_text to avoid this, but it's not worth the trouble.) struct sub_data and subreader.c are still around, even its ASS/SSA reader. But struct sub_data is freed right after converting it to ASS_Track. The internal ASS reader actually can handle some obscure cases libass can't, like files encoded in UTF-16.
This commit is contained in:
parent
f7ad81c0f5
commit
b44202b69f
1
Makefile
1
Makefile
@ -227,7 +227,6 @@ 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 \
|
||||
|
@ -1307,7 +1307,6 @@ static int mp_property_sub_visibility(m_option_t *prop, int action,
|
||||
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;
|
||||
@ -1991,26 +1990,18 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
||||
}
|
||||
|
||||
case MP_CMD_SUB_STEP:
|
||||
#ifdef CONFIG_ASS
|
||||
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 (ass_track) {
|
||||
available = true;
|
||||
set_osd_tmsg(mpctx, OSD_MSG_SUB_DELAY, osdl, osd_duration,
|
||||
"Sub delay: %d ms", ROUND(sub_delay * 1000));
|
||||
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));
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case MP_CMD_OSD: {
|
||||
@ -2194,7 +2185,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 +2222,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 +2254,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
|
||||
|
@ -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"
|
||||
|
||||
@ -112,9 +110,6 @@ struct track {
|
||||
// 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;
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -129,7 +124,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
|
||||
|
145
core/mplayer.c
145
core/mplayer.c
@ -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"
|
||||
@ -284,8 +285,6 @@ static void print_stream(struct MPContext *mpctx, struct track *t)
|
||||
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)");
|
||||
@ -1052,38 +1051,36 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename,
|
||||
float fps, int noerr)
|
||||
{
|
||||
struct MPOpts *opts = &mpctx->opts;
|
||||
sub_data *subd = NULL;
|
||||
struct sh_sub *sh = NULL;
|
||||
struct ass_track *asst = NULL;
|
||||
const char *codec = 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;
|
||||
if (opts->ass_enabled) {
|
||||
asst = mp_ass_read_stream(mpctx->ass_library, filename, sub_cp);
|
||||
codec = "ass";
|
||||
}
|
||||
if (!asst) {
|
||||
sub_data *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);
|
||||
}
|
||||
if (asst)
|
||||
sh = sd_ass_create_from_track(asst, codec, opts);
|
||||
#endif
|
||||
} else
|
||||
subd = sub_read_file(filename, fps, &mpctx->opts);
|
||||
|
||||
|
||||
if (!sh && !subd) {
|
||||
if (!sh) {
|
||||
// Used with image subtitles.
|
||||
struct track *ext = open_external_file(mpctx, filename, NULL, 0,
|
||||
STREAM_SUB);
|
||||
if (ext)
|
||||
@ -1101,7 +1098,6 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename,
|
||||
.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);
|
||||
@ -1545,23 +1541,16 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1877,9 +1866,7 @@ 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);
|
||||
set_osd_subtitle(mpctx, NULL);
|
||||
if (vo_spudec) {
|
||||
spudec_reset(vo_spudec);
|
||||
vo_osd_changed(OSDTYPE_SPU);
|
||||
@ -1888,33 +1875,22 @@ static void reset_subtitles(struct MPContext *mpctx)
|
||||
|
||||
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)
|
||||
return;
|
||||
|
||||
if (!track->under_timeline)
|
||||
mpctx->osd->sub_offset = 0;
|
||||
double video_offset = track->under_timeline ? mpctx->video_offset : 0;
|
||||
|
||||
double refpts_s = refpts_tl - mpctx->osd->sub_offset;
|
||||
double curpts_s = refpts_s + sub_delay;
|
||||
mpctx->osd->sub_offset = video_offset - sub_delay;
|
||||
|
||||
// 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));
|
||||
}
|
||||
double curpts_s = refpts_tl - mpctx->osd->sub_offset;
|
||||
double refpts_s = refpts_tl - video_offset;
|
||||
|
||||
// DVD sub:
|
||||
if (is_dvd_sub(type) && !(sh_sub && sh_sub->active)) {
|
||||
@ -1955,7 +1931,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
|
||||
if (timestamp >= 0)
|
||||
spudec_assemble(vo_spudec, packet, len, timestamp);
|
||||
}
|
||||
} else if (d_sub && (is_text_sub(type) || (sh_sub && sh_sub->active))) {
|
||||
} else if (d_sub && sh_sub && sh_sub->active) {
|
||||
bool non_interleaved = is_non_interleaved(mpctx, track);
|
||||
if (non_interleaved)
|
||||
ds_get_next_pts(d_sub);
|
||||
@ -1967,7 +1943,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
|
||||
"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(sh_sub))
|
||||
break;
|
||||
// Try to avoid demuxing whole file at once
|
||||
if (non_interleaved && subpts_s > curpts_s + 1)
|
||||
@ -1984,41 +1960,19 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
|
||||
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);
|
||||
}
|
||||
sub_decode(sh_sub, mpctx->osd, packet, len, subpts_s, duration);
|
||||
if (non_interleaved)
|
||||
ds_get_next_pts(d_sub);
|
||||
}
|
||||
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)
|
||||
set_osd_subtitle(mpctx, sub_get_text(mpctx->osd, curpts_s));
|
||||
}
|
||||
|
||||
static int check_framedrop(struct MPContext *mpctx, double frame_time)
|
||||
@ -2130,12 +2084,8 @@ static void reinit_subs(struct MPContext *mpctx)
|
||||
|
||||
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);
|
||||
if (track->sh_sub) {
|
||||
sub_init(track->sh_sub, mpctx->osd);
|
||||
} else if (track->stream) {
|
||||
struct stream *s = track->demuxer ? track->demuxer->stream : NULL;
|
||||
if (s && s->type == STREAMTYPE_DVD)
|
||||
@ -2153,6 +2103,12 @@ static void reinit_subs(struct MPContext *mpctx)
|
||||
else
|
||||
sub_init(mpctx->sh_sub, mpctx->osd);
|
||||
}
|
||||
|
||||
// Decides whether to use OSD path or normal subtitle rendering path.
|
||||
mpctx->osd->render_bitmap_subs = true;
|
||||
struct sh_sub *sh_sub = mpctx->osd->sh_sub;
|
||||
if (sh_sub && sh_sub->active && sh_sub->sd_driver->get_text)
|
||||
mpctx->osd->render_bitmap_subs = opts->ass_enabled;
|
||||
}
|
||||
|
||||
static char *track_layout_hash(struct MPContext *mpctx)
|
||||
@ -4551,7 +4507,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);
|
||||
|
@ -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;
|
||||
|
@ -51,13 +51,13 @@ bool is_dvd_sub(const char *t)
|
||||
|
||||
void sub_init(struct sh_sub *sh, struct osd_state *osd)
|
||||
{
|
||||
struct MPOpts *opts = sh->opts;
|
||||
const char *format = sh->gsh->codec;
|
||||
|
||||
assert(!osd->sh_sub);
|
||||
if (sd_lavc.probe(sh))
|
||||
if (sd_lavc.supports_format(format))
|
||||
sh->sd_driver = &sd_lavc;
|
||||
#ifdef CONFIG_ASS
|
||||
if (opts->ass_enabled && sd_ass.probe(sh))
|
||||
if (sd_ass.supports_format(format))
|
||||
sh->sd_driver = &sd_ass;
|
||||
#endif
|
||||
if (sh->sd_driver) {
|
||||
@ -70,6 +70,11 @@ void sub_init(struct sh_sub *sh, struct osd_state *osd)
|
||||
}
|
||||
}
|
||||
|
||||
bool sub_accept_packets_in_advance(struct sh_sub *sh)
|
||||
{
|
||||
return sh->active && sh->sd_driver->accept_packets_in_advance;
|
||||
}
|
||||
|
||||
void sub_decode(struct sh_sub *sh, struct osd_state *osd, void *data,
|
||||
int data_len, double pts, double duration)
|
||||
{
|
||||
@ -101,6 +106,19 @@ void sub_get_bitmaps(struct osd_state *osd, struct mp_osd_res dim, double pts,
|
||||
osd->switch_sub_id = 0;
|
||||
}
|
||||
|
||||
char *sub_get_text(struct osd_state *osd, double pts)
|
||||
{
|
||||
struct MPOpts *opts = osd->opts;
|
||||
char *text = NULL;
|
||||
if (!opts->sub_visibility || !osd->sh_sub || !osd->sh_sub->active) {
|
||||
// -
|
||||
} else {
|
||||
if (osd->sh_sub->sd_driver->get_text)
|
||||
text = osd->sh_sub->sd_driver->get_text(osd->sh_sub, osd, pts);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
void sub_reset(struct sh_sub *sh, struct osd_state *osd)
|
||||
{
|
||||
if (sh->active && sh->sd_driver->reset)
|
||||
|
@ -14,18 +14,19 @@ bool is_text_sub(const char *t);
|
||||
bool is_ass_sub(const char *t);
|
||||
bool is_dvd_sub(const char *t);
|
||||
|
||||
bool sub_accept_packets_in_advance(struct sh_sub *sh);
|
||||
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 sub_bitmaps *res);
|
||||
char *sub_get_text(struct osd_state *osd, double pts);
|
||||
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);
|
||||
|
||||
struct sh_sub *sd_ass_create_from_track(struct ass_track *track,
|
||||
bool vsfilter_aspect,
|
||||
struct MPOpts *opts);
|
||||
const char *codec, struct MPOpts *opts);
|
||||
|
||||
#ifdef CONFIG_ASS
|
||||
struct ass_track *sub_get_ass_track(struct osd_state *osd);
|
||||
|
174
sub/find_sub.c
174
sub/find_sub.c
@ -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);
|
||||
}
|
@ -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)
|
||||
@ -370,15 +370,9 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj)
|
||||
ass_set_line_position(osd->osd_render, 100 - 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:
|
||||
|
4
sub/sd.h
4
sub/sd.h
@ -4,13 +4,15 @@
|
||||
#include "dec_sub.h"
|
||||
|
||||
struct sd_functions {
|
||||
bool (*probe)(struct sh_sub *sh);
|
||||
bool accept_packets_in_advance;
|
||||
bool (*supports_format)(const char *format);
|
||||
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);
|
||||
char *(*get_text)(struct sh_sub *sh, struct osd_state *osd, double pts);
|
||||
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);
|
||||
|
102
sub/sd_ass.c
102
sub/sd_ass.c
@ -17,10 +17,11 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ass/ass.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ass/ass.h>
|
||||
|
||||
#include "talloc.h"
|
||||
|
||||
#include "core/options.h"
|
||||
@ -39,11 +40,12 @@ 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 supports_format(const char *format)
|
||||
{
|
||||
return is_text_sub(sh->gsh->codec);
|
||||
return is_text_sub(format);
|
||||
}
|
||||
|
||||
static void free_last_event(ASS_Track *track)
|
||||
@ -163,6 +165,89 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
|
||||
talloc_steal(ctx, ctx->parts);
|
||||
}
|
||||
|
||||
struct buf {
|
||||
char *start;
|
||||
int size;
|
||||
int len;
|
||||
};
|
||||
|
||||
static void append(struct buf *b, char c)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char *get_text(struct sh_sub *sh, struct osd_state *osd, double pts)
|
||||
{
|
||||
struct sd_ass_priv *ctx = sh->context;
|
||||
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) {
|
||||
ass_to_plaintext(&b, event->Text);
|
||||
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 reset(struct sh_sub *sh, struct osd_state *osd)
|
||||
{
|
||||
struct sd_ass_priv *ctx = sh->context;
|
||||
@ -183,10 +268,12 @@ static void uninit(struct sh_sub *sh)
|
||||
}
|
||||
|
||||
const struct sd_functions sd_ass = {
|
||||
.probe = probe,
|
||||
.accept_packets_in_advance = true,
|
||||
.supports_format = supports_format,
|
||||
.init = init,
|
||||
.decode = decode,
|
||||
.get_bitmaps = get_bitmaps,
|
||||
.get_text = get_text,
|
||||
.reset = reset,
|
||||
.switch_off = reset,
|
||||
.uninit = uninit,
|
||||
@ -199,20 +286,19 @@ static int sd_ass_track_destructor(void *ptr)
|
||||
}
|
||||
|
||||
struct sh_sub *sd_ass_create_from_track(struct ass_track *track,
|
||||
bool vsfilter_aspect,
|
||||
struct MPOpts *opts)
|
||||
const char *codec, 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",
|
||||
.codec = codec,
|
||||
}),
|
||||
.sd_driver = &sd_ass,
|
||||
.context = talloc_struct(sh, struct sd_ass_priv, {
|
||||
.ass_track = track,
|
||||
.vsfilter_aspect = vsfilter_aspect,
|
||||
.vsfilter_aspect = is_ass_sub(codec),
|
||||
}),
|
||||
.initialized = true,
|
||||
};
|
||||
|
@ -41,9 +41,9 @@ 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:
|
||||
@ -241,7 +241,7 @@ static void uninit(struct sh_sub *sh)
|
||||
}
|
||||
|
||||
const struct sd_functions sd_lavc = {
|
||||
.probe = probe,
|
||||
.supports_format = supports_format,
|
||||
.init = init,
|
||||
.decode = decode,
|
||||
.get_bitmaps = get_bitmaps,
|
||||
|
39
sub/sub.c
39
sub/sub.c
@ -45,8 +45,6 @@
|
||||
int sub_pos=100;
|
||||
int sub_visibility=1;
|
||||
|
||||
subtitle* vo_sub=NULL;
|
||||
|
||||
float sub_delay = 0;
|
||||
float sub_fps = 0;
|
||||
|
||||
@ -103,6 +101,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,
|
||||
};
|
||||
|
||||
@ -117,7 +116,7 @@ struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib)
|
||||
|
||||
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;
|
||||
@ -133,15 +132,27 @@ void osd_free(struct osd_state *osd)
|
||||
global_osd = NULL;
|
||||
}
|
||||
|
||||
void osd_set_text(struct osd_state *osd, const char *text)
|
||||
static bool set_text(void *talloc_ctx, char **var, 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 (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 (!set_text(osd, &osd->osd_text, text))
|
||||
vo_osd_changed(OSDTYPE_OSD);
|
||||
}
|
||||
|
||||
void osd_set_sub(struct osd_state *osd, const char *text)
|
||||
{
|
||||
if (!set_text(osd, &osd->sub_text, text))
|
||||
vo_osd_changed(OSDTYPE_SUBTEXT);
|
||||
}
|
||||
|
||||
static bool spu_visible(struct osd_state *osd, struct osd_object *obj)
|
||||
@ -172,10 +183,12 @@ static void render_object(struct osd_state *osd, struct osd_object *obj,
|
||||
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 (osd->render_bitmap_subs) {
|
||||
double sub_pts = video_pts;
|
||||
if (sub_pts != MP_NOPTS_VALUE)
|
||||
sub_pts -= osd->sub_offset;
|
||||
sub_get_bitmaps(osd, obj->vo_res, sub_pts, out_imgs);
|
||||
}
|
||||
} else {
|
||||
osd_object_get_bitmaps(osd, obj, out_imgs);
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ struct mp_osd_res {
|
||||
|
||||
enum mp_osdtype {
|
||||
OSDTYPE_SUB,
|
||||
OSDTYPE_SUBTITLE,
|
||||
OSDTYPE_SUBTEXT,
|
||||
OSDTYPE_SPU,
|
||||
|
||||
OSDTYPE_PROGBAR,
|
||||
@ -126,11 +126,14 @@ struct osd_state {
|
||||
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
|
||||
@ -149,8 +152,6 @@ struct osd_state {
|
||||
struct ass_library *osd_ass_library;
|
||||
};
|
||||
|
||||
extern struct subtitle* vo_sub;
|
||||
|
||||
extern void* vo_spudec;
|
||||
extern void* vo_vobsub;
|
||||
|
||||
@ -206,6 +207,7 @@ 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 osd_set_sub(struct osd_state *osd, const char *text);
|
||||
void vo_osd_changed(int new_value);
|
||||
void osd_changed_all(struct osd_state *osd);
|
||||
void osd_free(struct osd_state *osd);
|
||||
|
102
sub/subreader.c
102
sub/subreader.c
@ -1684,7 +1684,7 @@ if ((suboverlap_enabled == 2) ||
|
||||
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 = "text"; //srp->name;
|
||||
subt_data->filename = strdup(filename);
|
||||
subt_data->sub_uses_time = uses_time;
|
||||
subt_data->sub_num = sub_num;
|
||||
@ -1704,103 +1704,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;
|
||||
}
|
||||
|
@ -67,7 +67,6 @@ typedef struct subtitle {
|
||||
unsigned long end;
|
||||
|
||||
char *text[SUB_MAX_TEXT];
|
||||
double endpts[SUB_MAX_TEXT];
|
||||
unsigned char alignment;
|
||||
} subtitle;
|
||||
|
||||
@ -92,10 +91,5 @@ void subcp_close (void); /* for demux_ogg.c */
|
||||
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user