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:
wm4 2013-04-28 21:12:11 +02:00
parent f7ad81c0f5
commit b44202b69f
16 changed files with 213 additions and 444 deletions

View File

@ -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 \

View File

@ -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

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"
@ -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

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"
@ -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);

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

@ -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)

View File

@ -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);

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

@ -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:

View File

@ -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);

View File

@ -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,
};

View File

@ -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,

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */