mp_msg: print messages to stdout, statusline to stderr
[mplayer.git] / sub / sd_ass.c
blob1cdc1e5f37293872295335d0c6952212e7a16f55
1 /*
2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include <stdlib.h>
20 #include <ass/ass.h>
21 #include <assert.h>
22 #include <string.h>
24 #include "talloc.h"
26 #include "options.h"
27 #include "mpcommon.h"
28 #include "mp_msg.h"
29 #include "libmpdemux/stheader.h"
30 #include "sub.h"
31 #include "ass_mp.h"
32 #include "sd.h"
33 #include "subassconvert.h"
35 struct sd_ass_priv {
36 struct ass_track *ass_track;
37 bool vsfilter_aspect;
38 bool incomplete_event;
41 static void free_last_event(ASS_Track *track)
43 assert(track->n_events > 0);
44 ass_free_event(track, track->n_events - 1);
45 track->n_events--;
48 static int init(struct sh_sub *sh, struct osd_state *osd)
50 struct sd_ass_priv *ctx;
52 if (sh->initialized) {
53 ctx = sh->context;
54 } else {
55 ctx = talloc_zero(NULL, struct sd_ass_priv);
56 sh->context = ctx;
57 if (sh->type == 'a') {
58 ctx->ass_track = ass_new_track(osd->ass_library);
59 if (sh->extradata)
60 ass_process_codec_private(ctx->ass_track, sh->extradata,
61 sh->extradata_len);
62 } else
63 ctx->ass_track = mp_ass_default_track(osd->ass_library, sh->opts);
66 ctx->vsfilter_aspect = sh->type == 'a';
67 return 0;
70 static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
71 int data_len, double pts, double duration)
73 unsigned char *text = data;
74 struct sd_ass_priv *ctx = sh->context;
75 ASS_Track *track = ctx->ass_track;
77 if (sh->type == 'a') { // ssa/ass subs
78 ass_process_chunk(track, data, data_len,
79 (long long)(pts*1000 + 0.5),
80 (long long)(duration*1000 + 0.5));
81 return;
83 // plaintext subs
84 if (pts == MP_NOPTS_VALUE) {
85 mp_msg(MSGT_SUBREADER, MSGL_WARN, "Subtitle without pts, ignored\n");
86 return;
88 long long ipts = pts * 1000 + 0.5;
89 long long iduration = duration * 1000 + 0.5;
90 if (ctx->incomplete_event) {
91 ctx->incomplete_event = false;
92 ASS_Event *event = track->events + track->n_events - 1;
93 if (ipts <= event->Start)
94 free_last_event(track);
95 else
96 event->Duration = ipts - event->Start;
98 // Note: we rely on there being guaranteed 0 bytes after data packets
99 int len = strlen(text);
100 if (len < 5) {
101 // Some tracks use a whitespace (but not empty) packet to mark end
102 // of previous subtitle.
103 for (int i = 0; i < len; i++)
104 if (!strchr(" \f\n\r\t\v", text[i]))
105 goto not_all_whitespace;
106 return;
108 not_all_whitespace:;
109 char buf[500];
110 subassconvert_subrip(text, buf, sizeof(buf));
111 for (int i = 0; i < track->n_events; i++)
112 if (track->events[i].Start == ipts
113 && (duration <= 0 || track->events[i].Duration == iduration)
114 && strcmp(track->events[i].Text, buf) == 0)
115 return; // We've already added this subtitle
116 if (duration <= 0) {
117 iduration = 10000;
118 ctx->incomplete_event = true;
120 int eid = ass_alloc_event(track);
121 ASS_Event *event = track->events + eid;
122 event->Start = ipts;
123 event->Duration = iduration;
124 event->Style = track->default_style;
125 event->Text = strdup(buf);
128 static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
129 struct sub_bitmaps *res)
131 struct sd_ass_priv *ctx = sh->context;
132 struct MPOpts *opts = osd->opts;
134 if (osd->sub_pts == MP_NOPTS_VALUE)
135 return;
137 double scale = osd->normal_scale;
138 if (ctx->vsfilter_aspect && opts->ass_vsfilter_aspect_compat)
139 scale = osd->vsfilter_scale;
140 ASS_Renderer *renderer = osd->ass_renderer;
141 mp_ass_configure(renderer, opts, &osd->dim, osd->unscaled);
142 ass_set_aspect_ratio(renderer, scale, 1);
143 int changed;
144 res->imgs = ass_render_frame(renderer, ctx->ass_track,
145 osd->sub_pts * 1000 + .5, &changed);
146 if (changed == 2)
147 res->bitmap_id = ++res->bitmap_pos_id;
148 else if (changed)
149 res->bitmap_pos_id++;
150 res->type = SUBBITMAP_LIBASS;
153 static void reset(struct sh_sub *sh, struct osd_state *osd)
155 struct sd_ass_priv *ctx = sh->context;
156 if (ctx->incomplete_event)
157 free_last_event(ctx->ass_track);
158 ctx->incomplete_event = false;
161 static void uninit(struct sh_sub *sh)
163 struct sd_ass_priv *ctx = sh->context;
165 ass_free_track(ctx->ass_track);
166 talloc_free(ctx);
169 const struct sd_functions sd_ass = {
170 .init = init,
171 .decode = decode,
172 .get_bitmaps = get_bitmaps,
173 .reset = reset,
174 .switch_off = reset,
175 .uninit = uninit,
179 struct sh_sub *sd_ass_create_from_track(struct ass_track *track,
180 bool vsfilter_aspect,
181 struct MPOpts *opts)
183 struct sh_sub *sh = talloc(NULL, struct sh_sub);
184 sh->opts = opts;
185 sh->type = 'a';
186 sh->title = track->name;
187 sh->sd_driver = &sd_ass;
188 struct sd_ass_priv *ctx = talloc_zero(sh, struct sd_ass_priv);
189 sh->context = ctx;
190 ctx->ass_track = track;
191 ctx->vsfilter_aspect = vsfilter_aspect;
192 sh->initialized = true;
193 return sh;