[MPlayer-dev-eng] [PATCH] Simplify and factorize EOSD code
Nicolas George
nicolas.george at normalesup.org
Thu Feb 26 00:34:10 CET 2009
Before answering to your remarks, I will introduce the new version of this
patch.
It corrects the PTS problem, and it actually adds a new visible feature.
That feature is an annoying ball continuously sliding from one side of the
window to the other. And it can not be disabled.
Obviously, this is not for inclusion; I leave it as a proof of concept and
testing material. The point is that, with about 120 additional lines of
code, it is now possible, from anywhere in mplayer, to overlay a piece of
graphics on the video.
That means that if someone, tomorrow, wants to implement DVB subtitles, he
would obviously have to hack the demuxers and make a decoder (or use the
lavc codec), but then the display part would be already done.
Le septidi 7 ventôse, an CCXVII, Uoti Urpala a écrit :
> The patch already had rather separate-looking changes like all the
> libass interface stuff, though I didn't verify whether there was some
> reason they'd naturally be part of the same development effort (at least
> they _could_ be split if you wanted to).
They fit the description: simplify and factorize.
> I think the changes to VDPAU are straightforward enough to make this
> argument invalid.
That is true, and VDPAU evolution is actually slower than I thought. And I
was faster than I expected.
> Of course. But I think the pts handling error was already quite
> significant, and I wouldn't trust you to understand all related things
> well enough to necessarily create a good result.
If there are things that I do not understand, there are probably other
people who misunderstand them too: I have the hubris of thinking that I am
not so much below the median level of the open source developers.
In that case, making things more simple when then can, and documenting them
when they can not, just for the sake of making things more simple, is
already a worthy effort.
Furthermore, making EOSD capable of handling several sources of overlays is
the first step towards using it for the other subtitle display, and thus
getting rid of a code what, you say, is broken.
> One additional thing about the patch: is the per-"client" configure
> callback really necessary? I think "clients" could well just check for
> changes before producing the actual bitmaps, and that could give simpler
> code than getting a callback at any moment.
Parts of code checking if things have changed is, IMHO, a very ugly design.
In particular, it leads inevitably to code duplication: each client has to
keep a copy of the old parameters and compare them on its own.
I would consider that design if it was actually shorter or simpler, but the
complete callback system is actually just fifteen lines of simple code.
Doing the tests in just one client would require almost as much.
> You still got the pts handling wrong. And you can't rely on the existing
> subtitle code being sane - not all parts of it are. Understanding what
> it does is not enough to understand how things _should_ work.
I corrected the PTS handling for the EOSD part, and the new version of the
patch does not touch the existing subtitle code, apart from a function
rename and an additional comment.
> You can create such a code path with much simpler changes than what were
> in the patch - and simple enough that the extra work is not much even if
> things are changed later. Just getting the image to renderers doesn't
> take all that many changes (the renderer changes would take more if you
> implement support in all of them, but that's about the same in any
> approach and your patch did no part of renderer support either). Yes,
> the result of doing that in a simple way will probably result in some
> kind of ugliness. But I'm not sure it would result in so much ugliness
> that that alone would justify adding a lot of extra API stuff. And it
> would allow making later "simplification" changes in a context where you
> could see whether they actually simplify things instead of just
> promising possible future benefit.
I still think that the original version of my patch actually simplified the
code a bit. At the very least, we have currently two code architectures that
leads from the subtitles to the renderer:
- vf_ass always pulls them from libass;
- the main loop emits a control, vf_vo reacts to the control by pulling the
subtitles from libass and emits them in another control, the video output
driver reacts to that control.
The patch I submitted makes it just one architecture, the same for all.
Regards,
--
Nicolas George
-------------- next part --------------
Makefile | 1 +
eosd.c | 104 +++++++++++++++++++++++
eosd.h | 123 ++++++++++++++++++++++++++++
libass/ass.c | 6 +-
libass/ass.h | 2 +
libass/ass_mp.c | 221 +++++++++++++++++++++++++++++++-------------------
libass/ass_mp.h | 23 +++---
libmpcodecs/vf_ass.c | 39 ++++-----
libmpcodecs/vf_vo.c | 62 ++------------
libmpdemux/demuxer.c | 20 ++---
libvo/vo_gl.c | 39 +++++-----
libvo/vo_vdpau.c | 37 ++++-----
mpcommon.c | 5 +-
mplayer.c | 92 +++++++++++++++++-----
14 files changed, 532 insertions(+), 242 deletions(-)
diff --git a/Makefile b/Makefile
index 449b058..fa52118 100644
--- a/Makefile
+++ b/Makefile
@@ -37,6 +37,7 @@ SRCS_COMMON = asxparser.c \
codec-cfg.c \
cpudetect.c \
edl.c \
+ eosd.c \
find_sub.c \
fmt-conversion.c \
get_path.c \
diff --git a/eosd.c b/eosd.c
new file mode 100644
index 0000000..6e1ea01
--- /dev/null
+++ b/eosd.c
@@ -0,0 +1,104 @@
+/*
+ * EOSD subsystem
+ *
+ * Copyright (C) 2009 Nicolas George
+ *
+ * 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 "eosd.h"
+#include "mpcommon.h"
+#include "libvo/sub.h"
+
+#include "libass/ass_mp.h"
+
+static struct eosd_client *clients, **next_client = &clients;
+static mp_eosd_images_t images;
+
+void eosd_client_register(struct eosd_client *client)
+{
+ client->next = NULL;
+ client->images = NULL;
+ *next_client = client;
+ next_client = &client->next;
+}
+
+void eosd_client_set_images(struct eosd_client *c, ass_image_t *i, int changed)
+{
+ c->images = i;
+ if (changed > images.changed)
+ images.changed = changed;
+}
+
+void eosd_driver_configure(struct eosd_settings *s)
+{
+ struct eosd_client *c;
+ int inw, inh;
+
+ images.changed = 2;
+ inw = s->width - s->margin_left - s->margin_right;
+ inh = s->height - s->margin_top - s->margin_bottom;
+ if (s->inner_aspect_ratio != 0) {
+ s->pixel_aspect_ratio = s->inner_aspect_ratio * inh / inw;
+ s->outer_aspect_ratio = s->pixel_aspect_ratio * s->width / s->height;
+ } else if (s->outer_aspect_ratio != 0) {
+ s->pixel_aspect_ratio = s->outer_aspect_ratio * s->height / s->width;
+ s->inner_aspect_ratio = s->pixel_aspect_ratio * inw / inh;
+ } else {
+ if(s->pixel_aspect_ratio == 0)
+ s->pixel_aspect_ratio = 1;
+ s->outer_aspect_ratio = s->pixel_aspect_ratio * s->width / s->height;
+ s->inner_aspect_ratio = s->pixel_aspect_ratio * inw / inh;
+ }
+ for (c = clients; c != NULL; c = c->next)
+ c->configure(c, s);
+}
+
+void eosd_driver_set_pts(double pts)
+{
+ struct eosd_client *c;
+
+ for (c = clients; c != NULL; c = c->next)
+ if (c->update)
+ c->update(c, pts);
+}
+
+mp_eosd_images_t *eosd_driver_get_images(void)
+{
+ struct eosd_client *c;
+ ass_image_t *i, **tail;
+
+ tail = &images.imgs;
+ for (c = clients; c != NULL; c = c->next) {
+ *tail = c->images;
+ for (i = c->images; i != NULL; i = i->next)
+ tail = &i->next;
+ c->images_tail = tail;
+ }
+ *tail = NULL;
+ return &images;
+}
+
+void eosd_driver_release(void)
+{
+ struct eosd_client *c;
+
+ for (c = clients; c != NULL; c = c->next) {
+ *c->images_tail = NULL;
+ }
+ images.changed = 0;
+}
diff --git a/eosd.h b/eosd.h
new file mode 100644
index 0000000..f9086c9
--- /dev/null
+++ b/eosd.h
@@ -0,0 +1,123 @@
+/*
+ * EOSD subsystem
+ *
+ * Copyright (C) 2009 Nicolas George
+ *
+ * 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_EOSD_H
+#define MPLAYER_EOSD_H
+
+#include "libass/ass.h"
+
+/**
+ * EOSD driver settings
+ * This structure is used by the EOSD subsystem to notify the clients of the
+ * particular settings of the driver.
+ * The margins are counted inside the rendering area.
+ */
+struct eosd_settings {
+ int width;
+ int height;
+ double inner_aspect_ratio;
+ double outer_aspect_ratio;
+ double pixel_aspect_ratio;
+ int unscaled; ///< if set, the overlay will not be scaled again
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+};
+
+#define EOSD_SETTINGS_CLEAR ((struct eosd_settings){ })
+
+/**
+ * EOSD client handle
+ * An EOSD client must create and initialize an instance of this structure
+ * in order to be able to register to the EOSD subsystem.
+ */
+struct eosd_client {
+
+ struct eosd_client *next; ///< private
+ ass_image_t *images, **images_tail; ///< private
+
+ /**
+ * The configure callback will be called whenever the settings of the
+ * driver are known or change.
+ * When this happens, the EOSD subsystem is blanked, and all objects
+ * must be recomputed and added.
+ */
+ void (*configure)(struct eosd_client *, const struct eosd_settings *);
+
+ /**
+ * The update callback will be called at each frame.
+ * It gets the current timestamp as an argument.
+ */
+ void (*update)(struct eosd_client *, double);
+};
+
+/**
+ * EOSD image list
+ */
+typedef struct {
+ ass_image_t *imgs; ///< linked list of all images
+ int changed; ///< 0: nothing, 1: only coordinates, 2: anything
+} mp_eosd_images_t;
+
+/**
+ * Registers a client to the EOSD subsystem
+ * The eosd_client structure must never be freed.
+ * It can be a part of a bigger structure.
+ */
+void eosd_client_register(struct eosd_client *);
+
+/**
+ * Sets the images to be shown by the EOSD subsystem
+ * @param client: owner of the images
+ * @param images: linked list of images
+ * @param changed: what changed since the previous call:
+ * 0: nothing, 1: only coordinates, 2: anything
+ * @note This is (hopefully) a temporary API, since it does not allow to
+ * merge the images from various clients.
+ */
+void eosd_client_set_images(struct eosd_client *client, ass_image_t *images,
+ int changed);
+
+/**
+ * Configures the output settings of the driver
+ * Only one aspect ratio must be set, the others will be derived from it.
+ */
+void eosd_driver_configure(struct eosd_settings *);
+
+/**
+ * Sets the current timestamp and let the clients update their objects
+ */
+void eosd_driver_set_pts(double);
+
+/**
+ * Gets the list of currently shown objects
+ */
+mp_eosd_images_t *eosd_driver_get_images(void);
+
+/**
+ * Releases the objects after they have been rendered
+ * Currently, this only reset the changed field.
+ */
+void eosd_driver_release(void);
+
+#endif /* MPLAYER_EOSD_H */
diff --git a/libass/ass.c b/libass/ass.c
index 8022dfc..fc900a9 100644
--- a/libass/ass.c
+++ b/libass/ass.c
@@ -324,7 +324,7 @@ static int process_event_tail(ass_track_t* track, ass_event_t* event, char* str,
* \param track track to apply overrides to
* The format for overrides is [StyleName.]Field=Value
*/
-void process_force_style(ass_track_t* track) {
+void ass_process_force_style(ass_track_t* track) {
char **fs, *eq, *dt, *style, *tname, *token;
ass_style_t* target;
int sid;
@@ -755,7 +755,7 @@ void ass_process_codec_private(ass_track_t* track, char *data, int size)
track->event_format = strdup("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text");
}
- process_force_style(track);
+ ass_process_force_style(track);
}
static int check_duplicate_event(ass_track_t* track, int ReadOrder)
@@ -982,7 +982,7 @@ static ass_track_t* parse_memory(ass_library_t* library, char* buf)
return 0;
}
- process_force_style(track);
+ ass_process_force_style(track);
return track;
}
diff --git a/libass/ass.h b/libass/ass.h
index 4bdea3a..0e1dbf0 100644
--- a/libass/ass.h
+++ b/libass/ass.h
@@ -145,6 +145,8 @@ int ass_alloc_event(ass_track_t* track);
*/
void ass_free_style(ass_track_t* track, int sid);
+void ass_process_force_style(ass_track_t* track);
+
/**
* \brief delete an event
* \param track track
diff --git a/libass/ass_mp.c b/libass/ass_mp.c
index 40da63c..5eba498 100644
--- a/libass/ass_mp.c
+++ b/libass/ass_mp.c
@@ -26,6 +26,9 @@
#include "mp_msg.h"
#include "get_path.h"
+#include "libvo/sub.h"
+#include "eosd.h"
+#include "mpcommon.h"
#include "ass.h"
#include "ass_utils.h"
@@ -36,8 +39,11 @@
#include <fontconfig/fontconfig.h>
#endif
+static ass_library_t* ass_library;
+static ass_renderer_t* ass_renderer;
+static struct eosd_client ass_eosd_client;
+
// libass-related command line options
-ass_library_t* ass_library;
int ass_enabled = 0;
float ass_font_scale = 1.;
float ass_line_spacing = 0.;
@@ -71,10 +77,99 @@ extern char* sub_cp;
static char* sub_cp = 0;
#endif
-void process_force_style(ass_track_t* track);
+static void ass_mp_configure(struct eosd_client *client, const struct eosd_settings *settings) {
+ int hinting;
+ (void)client;
+
+ ass_set_frame_size(ass_renderer, settings->width, settings->height);
+ ass_set_margins(ass_renderer, settings->margin_top, settings->margin_bottom, 0, 0);
+ ass_set_use_margins(ass_renderer, ass_use_margins);
+ ass_set_font_scale(ass_renderer, ass_font_scale);
+ if (!settings->unscaled && (ass_hinting & 4))
+ hinting = 0;
+ else
+ hinting = ass_hinting & 3;
+ ass_set_hinting(ass_renderer, hinting);
+ ass_set_line_spacing(ass_renderer, ass_line_spacing);
+ ass_set_aspect_ratio(ass_renderer, settings->outer_aspect_ratio);
+}
+
+static void ass_mp_update(struct eosd_client *client, double pts) {
+ ass_image_t *images;
+ int changed;
+ (void)client;
+
+ if(ass_track == NULL || ass_renderer == NULL || !sub_visibility) {
+ eosd_client_set_images(&ass_eosd_client, NULL, changed);
+ return;
+ }
+ if (ass_force_reload) {
+ ass_set_margins(ass_renderer, ass_top_margin, ass_bottom_margin, 0, 0);
+ ass_set_use_margins(ass_renderer, ass_use_margins);
+ ass_set_font_scale(ass_renderer, ass_font_scale);
+ ass_force_reload = 0;
+ }
+ images = ass_render_frame(ass_renderer, ass_track, pts * 1000 + .5, &changed);
+ eosd_client_set_images(&ass_eosd_client, images, changed);
+}
+
+static void ass_mp_configure_fonts(void) {
+ char *dir, *path, *family;
+ dir = get_path("fonts");
+ if (font_fontconfig < 0 && sub_font_name) path = strdup(sub_font_name);
+ else if (font_fontconfig < 0 && font_name) path = strdup(font_name);
+ else path = get_path("subfont.ttf");
+ if (font_fontconfig >= 0 && sub_font_name) family = strdup(sub_font_name);
+ else if (font_fontconfig >= 0 && font_name) family = strdup(font_name);
+ else family = 0;
+
+ if (font_fontconfig >= 0)
+ ass_set_fonts(ass_renderer, path, family);
+ else
+ ass_set_fonts_nofc(ass_renderer, path, family);
+
+ free(dir);
+ free(path);
+ free(family);
+}
+
+void ass_mp_library_init(void) {
+ if (ass_enabled) {
+ char* path = get_path("fonts");
+ ass_library = ass_library_init();
+ ass_set_fonts_dir(ass_library, path);
+ ass_set_extract_fonts(ass_library, extract_embedded_fonts);
+ ass_set_style_overrides(ass_library, ass_force_style_list);
+ free(path);
+ ass_eosd_client.configure = ass_mp_configure;
+ ass_eosd_client.update = ass_mp_update;
+ eosd_client_register(&ass_eosd_client);
+ }
+}
+
+void ass_mp_library_done(void) {
+ if (ass_library)
+ ass_library_done(ass_library);
+ ass_library = NULL;
+}
-ass_track_t* ass_default_track(ass_library_t* library) {
- ass_track_t* track = ass_new_track(library);
+void ass_mp_renderer_init(void) {
+ ass_renderer = ass_renderer_init(ass_library);
+ ass_mp_configure_fonts();
+}
+
+void ass_mp_renderer_done(void) {
+ if (ass_renderer)
+ ass_renderer_done(ass_renderer);
+ ass_renderer = NULL;
+}
+
+ass_track_t* ass_mp_new_track(void) {
+ return ass_new_track(ass_library);
+}
+
+ass_track_t* ass_mp_default_track(void) {
+ ass_track_t* track = ass_new_track(ass_library);
track->track_type = TRACK_TYPE_ASS;
track->Timer = 100.;
@@ -122,10 +217,17 @@ ass_track_t* ass_default_track(ass_library_t* library) {
style->ScaleY = 1.;
}
- process_force_style(track);
+ ass_process_force_style(track);
return track;
}
+ass_track_t* ass_mp_read_file(char* fname, char* codepage) {
+ if (ass_enabled)
+ return ass_read_file(ass_library, fname, codepage);
+ else
+ return NULL;
+}
+
static int check_duplicate_plaintext_event(ass_track_t* track)
{
int i;
@@ -140,6 +242,31 @@ static int check_duplicate_plaintext_event(ass_track_t* track)
}
/**
+ * \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_t* ass_mp_read_subdata(sub_data* subdata, double fps) {
+ ass_track_t* track;
+ int i;
+
+ track = ass_mp_default_track();
+ track->name = subdata->filename ? strdup(subdata->filename) : 0;
+
+ for (i = 0; i < subdata->sub_num; ++i) {
+ int eid = ass_mp_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;
+}
+
+/**
* \brief Convert subtitle to ass_event_t for the given track
* \param ass_track_t track
* \param sub subtitle to convert
@@ -147,7 +274,7 @@ static int check_duplicate_plaintext_event(ass_track_t* track)
* note: assumes that subtitle is _not_ fps-based; caller must manually correct
* Start and Duration in other case.
**/
-int ass_process_subtitle(ass_track_t* track, subtitle* sub)
+int ass_mp_process_subtitle(ass_track_t* track, subtitle* sub)
{
int eid;
ass_event_t* event;
@@ -194,85 +321,15 @@ int ass_process_subtitle(ass_track_t* track, subtitle* sub)
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_t* ass_read_subdata(ass_library_t* library, sub_data* subdata, double fps) {
- ass_track_t* track;
- int i;
-
- track = ass_default_track(library);
- 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;
- }
+void ass_mp_add_font(char* name, char* data, int data_size) {
+ if (ass_enabled && ass_library) {
+ ass_add_font(ass_library, name, data, data_size);
}
- return track;
}
-void ass_configure(ass_renderer_t* priv, int w, int h, int unscaled) {
- int hinting;
- ass_set_frame_size(priv, w, h);
- ass_set_margins(priv, ass_top_margin, ass_bottom_margin, 0, 0);
- ass_set_use_margins(priv, ass_use_margins);
- ass_set_font_scale(priv, ass_font_scale);
- if (!unscaled && (ass_hinting & 4))
- hinting = 0;
- else
- hinting = ass_hinting & 3;
- ass_set_hinting(priv, hinting);
- ass_set_line_spacing(priv, ass_line_spacing);
-}
-
-void ass_configure_fonts(ass_renderer_t* priv) {
- char *dir, *path, *family;
- dir = get_path("fonts");
- if (font_fontconfig < 0 && sub_font_name) path = strdup(sub_font_name);
- else if (font_fontconfig < 0 && font_name) path = strdup(font_name);
- else path = get_path("subfont.ttf");
- if (font_fontconfig >= 0 && sub_font_name) family = strdup(sub_font_name);
- else if (font_fontconfig >= 0 && font_name) family = strdup(font_name);
- else family = 0;
-
- if (font_fontconfig >= 0)
- ass_set_fonts(priv, path, family);
- else
- ass_set_fonts_nofc(priv, path, family);
-
- free(dir);
- free(path);
- free(family);
-}
-
-ass_library_t* ass_init(void) {
- ass_library_t* priv;
- char* path = get_path("fonts");
- priv = ass_library_init();
- ass_set_fonts_dir(priv, path);
- ass_set_extract_fonts(priv, extract_embedded_fonts);
- ass_set_style_overrides(priv, ass_force_style_list);
- free(path);
- return priv;
+void ass_mp_clear_fonts(void) {
+ if (ass_library)
+ ass_clear_fonts(ass_library);
}
int ass_force_reload = 0; // flag set if global ass-related settings were changed
-
-ass_image_t* ass_mp_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now, int* detect_change) {
- if (ass_force_reload) {
- ass_set_margins(priv, ass_top_margin, ass_bottom_margin, 0, 0);
- ass_set_use_margins(priv, ass_use_margins);
- ass_set_font_scale(priv, ass_font_scale);
- ass_force_reload = 0;
- }
- return ass_render_frame(priv, track, now, detect_change);
-}
diff --git a/libass/ass_mp.h b/libass/ass_mp.h
index 64b411a..0248ed7 100644
--- a/libass/ass_mp.h
+++ b/libass/ass_mp.h
@@ -27,7 +27,6 @@
#include "ass_types.h"
#include "ass.h"
-extern ass_library_t* ass_library;
extern int ass_enabled;
extern float ass_font_scale;
extern float ass_line_spacing;
@@ -41,20 +40,20 @@ extern char* ass_border_color;
extern char* ass_styles_file;
extern int ass_hinting;
-ass_track_t* ass_default_track(ass_library_t* library);
-int ass_process_subtitle(ass_track_t* track, subtitle* sub);
-ass_track_t* ass_read_subdata(ass_library_t* library, sub_data* subdata, double fps);
+void ass_mp_library_init(void);
+void ass_mp_library_done(void);
+void ass_mp_renderer_init(void);
+void ass_mp_renderer_done(void);
-void ass_configure(ass_renderer_t* priv, int w, int h, int hinting);
-void ass_configure_fonts(ass_renderer_t* priv);
-ass_library_t* ass_init(void);
+ass_track_t* ass_mp_new_track(void);
+ass_track_t* ass_mp_default_track(void);
+ass_track_t* ass_mp_read_file(char* fname, char* codepage);
+ass_track_t* ass_mp_read_subdata(sub_data* subdata, double fps);
+int ass_mp_process_subtitle(ass_track_t* track, subtitle* sub);
-typedef struct {
- ass_image_t* imgs;
- int changed;
-} mp_eosd_images_t;
+void ass_mp_add_font(char* name, char* data, int data_size);
+void ass_mp_clear_fonts(void);
extern int ass_force_reload;
-ass_image_t* ass_mp_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now, int* detect_change);
#endif /* LIBASS_MP_H */
diff --git a/libmpcodecs/vf_ass.c b/libmpcodecs/vf_ass.c
index f33aba4..315e2d8 100644
--- a/libmpcodecs/vf_ass.c
+++ b/libmpcodecs/vf_ass.c
@@ -41,7 +41,7 @@
#include "m_option.h"
#include "m_struct.h"
-#include "libass/ass.h"
+#include "eosd.h"
#include "libass/ass_mp.h"
#define _r(c) ((c)>>24)
@@ -62,8 +62,6 @@ static const struct vf_priv_s {
// 0 = insert always
int auto_insert;
- ass_renderer_t* ass_priv;
-
unsigned char* planes[3];
unsigned char* dirty_rows;
} vf_priv_dflt;
@@ -71,14 +69,12 @@ static const struct vf_priv_s {
extern int opt_screen_size_x;
extern int opt_screen_size_y;
-extern ass_track_t* ass_track;
-extern float sub_delay;
-extern int sub_visibility;
-
static int config(struct vf_instance_s* vf,
int width, int height, int d_width, int d_height,
unsigned int flags, unsigned int outfmt)
{
+ struct eosd_settings eosd = EOSD_SETTINGS_CLEAR;
+
if (outfmt == IMGFMT_IF09) return 0;
vf->priv->outh = height + ass_top_margin + ass_bottom_margin;
@@ -93,10 +89,15 @@ static int config(struct vf_instance_s* vf,
vf->priv->planes[2] = malloc(vf->priv->outw * vf->priv->outh);
vf->priv->dirty_rows = malloc(vf->priv->outh);
- if (vf->priv->ass_priv) {
- ass_configure(vf->priv->ass_priv, vf->priv->outw, vf->priv->outh, 0);
- ass_set_aspect_ratio(vf->priv->ass_priv, ((double)d_width) / d_height);
- }
+ eosd.width = width;
+ eosd.height = height + ass_top_margin + ass_bottom_margin;
+ eosd.margin_left = 0;
+ eosd.margin_right = 0;
+ eosd.margin_top = ass_top_margin;
+ eosd.margin_bottom = ass_bottom_margin;
+ eosd.inner_aspect_ratio = (double)d_width / d_height;
+ eosd.unscaled = 0;
+ eosd_driver_configure(&eosd);
return vf_next_config(vf, vf->priv->outw, vf->priv->outh, d_width, d_height, flags, outfmt);
}
@@ -326,12 +327,13 @@ static int render_frame(struct vf_instance_s* vf, mp_image_t *mpi, const ass_ima
static int put_image(struct vf_instance_s* vf, mp_image_t *mpi, double pts)
{
- ass_image_t* images = 0;
- if (sub_visibility && vf->priv->ass_priv && ass_track && (pts != MP_NOPTS_VALUE))
- images = ass_mp_render_frame(vf->priv->ass_priv, ass_track, (pts+sub_delay) * 1000 + .5, NULL);
+ mp_eosd_images_t *images;
+ eosd_driver_set_pts(pts);
+ images = eosd_driver_get_images();
prepare_image(vf, mpi);
- if (images) render_frame(vf, mpi, images);
+ if (images->imgs) render_frame(vf, mpi, images->imgs);
+ eosd_driver_release();
return vf_next_put_image(vf, vf->dmpi, pts);
}
@@ -351,12 +353,9 @@ static int control(vf_instance_t *vf, int request, void *data)
{
switch (request) {
case VFCTRL_INIT_EOSD:
- vf->priv->ass_priv = ass_renderer_init((ass_library_t*)data);
- if (!vf->priv->ass_priv) return CONTROL_FALSE;
- ass_configure_fonts(vf->priv->ass_priv);
return CONTROL_TRUE;
case VFCTRL_DRAW_EOSD:
- if (vf->priv->ass_priv) return CONTROL_TRUE;
+ return CONTROL_TRUE;
break;
}
return vf_next_control(vf, request, data);
@@ -364,8 +363,6 @@ static int control(vf_instance_t *vf, int request, void *data)
static void uninit(struct vf_instance_s* vf)
{
- if (vf->priv->ass_priv)
- ass_renderer_done(vf->priv->ass_priv);
if (vf->priv->planes[1])
free(vf->priv->planes[1]);
if (vf->priv->planes[2])
diff --git a/libmpcodecs/vf_vo.c b/libmpcodecs/vf_vo.c
index affffa6..e491ec1 100644
--- a/libmpcodecs/vf_vo.c
+++ b/libmpcodecs/vf_vo.c
@@ -10,11 +10,7 @@
#include "libvo/video_out.h"
-#ifdef CONFIG_ASS
-#include "libass/ass.h"
-#include "libass/ass_mp.h"
-extern ass_track_t* ass_track;
-#endif
+#include "eosd.h"
//===========================================================================//
@@ -24,10 +20,7 @@ extern float sub_delay;
struct vf_priv_s {
double pts;
const vo_functions_t *vo;
-#ifdef CONFIG_ASS
- ass_renderer_t* ass_priv;
- int prev_visibility;
-#endif
+ int do_eosd;
};
#define video_out (vf->priv->vo)
@@ -67,11 +60,6 @@ static int config(struct vf_instance_s* vf,
if(config_video_out(video_out,width,height,d_width,d_height,flags,"MPlayer",outfmt))
return 0;
-#ifdef CONFIG_ASS
- if (vf->priv->ass_priv)
- ass_configure(vf->priv->ass_priv, width, height, !!(vf->default_caps & VFCAP_EOSD_UNSCALED));
-#endif
-
++vo_config_count;
return 1;
}
@@ -113,45 +101,14 @@ static int control(struct vf_instance_s* vf, int request, void* data)
if(!vo_config_count) return CONTROL_FALSE; // vo not configured?
return (video_out->control(VOCTRL_GET_EQUALIZER, eq->item, &eq->value) == VO_TRUE) ? CONTROL_TRUE : CONTROL_FALSE;
}
-#ifdef CONFIG_ASS
case VFCTRL_INIT_EOSD:
- {
- vf->priv->ass_priv = ass_renderer_init((ass_library_t*)data);
- if (!vf->priv->ass_priv) return CONTROL_FALSE;
- ass_configure_fonts(vf->priv->ass_priv);
- vf->priv->prev_visibility = 0;
+ vf->priv->do_eosd = 1;
return CONTROL_TRUE;
- }
case VFCTRL_DRAW_EOSD:
- {
- mp_eosd_images_t images = {NULL, 2};
- double pts = vf->priv->pts;
- if (!vo_config_count || !vf->priv->ass_priv) return CONTROL_FALSE;
- if (sub_visibility && vf->priv->ass_priv && ass_track && (pts != MP_NOPTS_VALUE)) {
- mp_eosd_res_t res;
- memset(&res, 0, sizeof(res));
- if (video_out->control(VOCTRL_GET_EOSD_RES, &res) == VO_TRUE) {
- ass_set_frame_size(vf->priv->ass_priv, res.w, res.h);
- ass_set_margins(vf->priv->ass_priv, res.mt, res.mb, res.ml, res.mr);
- ass_set_aspect_ratio(vf->priv->ass_priv, (double)res.w / res.h);
- }
-
- images.imgs = ass_mp_render_frame(vf->priv->ass_priv, ass_track, (pts+sub_delay) * 1000 + .5, &images.changed);
- if (!vf->priv->prev_visibility)
- images.changed = 2;
- vf->priv->prev_visibility = 1;
- } else
- vf->priv->prev_visibility = 0;
- vf->priv->prev_visibility = sub_visibility;
- return (video_out->control(VOCTRL_DRAW_EOSD, &images) == VO_TRUE) ? CONTROL_TRUE : CONTROL_FALSE;
- }
-#endif
+ return (video_out->control(VOCTRL_DRAW_EOSD, NULL) == VO_TRUE) ? CONTROL_TRUE : CONTROL_FALSE;
case VFCTRL_GET_PTS:
- {
- *(double *)data = vf->priv->pts;
return CONTROL_TRUE;
}
- }
// return video_out->control(request,data);
return CONTROL_UNKNOWN;
}
@@ -180,6 +137,8 @@ static int put_image(struct vf_instance_s* vf,
// record pts (potentially modified by filters) for main loop
vf->priv->pts = pts;
// first check, maybe the vo/vf plugin implements draw_image using mpi:
+ if(vf->priv->do_eosd)
+ eosd_driver_set_pts(pts);
if(video_out->control(VOCTRL_DRAW_IMAGE,mpi)==VO_TRUE) return 1; // done.
// nope, fallback to old draw_frame/draw_slice:
if(!(mpi->flags&(MP_IMGFLAG_DIRECT|MP_IMGFLAG_DRAW_CALLBACK))){
@@ -207,13 +166,7 @@ static void draw_slice(struct vf_instance_s* vf,
static void uninit(struct vf_instance_s* vf)
{
- if (vf->priv) {
-#ifdef CONFIG_ASS
- if (vf->priv->ass_priv)
- ass_renderer_done(vf->priv->ass_priv);
-#endif
- free(vf->priv);
- }
+ free(vf->priv);
}
//===========================================================================//
@@ -228,6 +181,7 @@ static int open(vf_instance_t *vf, char* args){
vf->uninit=uninit;
vf->priv=calloc(1, sizeof(struct vf_priv_s));
vf->priv->vo = (const vo_functions_t *)args;
+ vf->priv->do_eosd = 0;
if(!video_out) return 0; // no vo ?
// if(video_out->preinit(args)) return 0; // preinit failed
return 1;
diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c
index 74bbd8e..420b1ea 100644
--- a/libmpdemux/demuxer.c
+++ b/libmpdemux/demuxer.c
@@ -911,17 +911,15 @@ static demuxer_t *demux_open_stream(stream_t *stream, int file_format,
sh_video->i_bps / 1024.0f);
}
#ifdef CONFIG_ASS
- if (ass_enabled && ass_library) {
- for (i = 0; i < MAX_S_STREAMS; ++i) {
- sh_sub_t *sh = demuxer->s_streams[i];
- if (sh && sh->type == 'a') {
- sh->ass_track = ass_new_track(ass_library);
- if (sh->ass_track && sh->extradata)
- ass_process_codec_private(sh->ass_track, sh->extradata,
- sh->extradata_len);
- } else if (sh && sh->type != 'v')
- sh->ass_track = ass_default_track(ass_library);
- }
+ for (i = 0; i < MAX_S_STREAMS; ++i) {
+ sh_sub_t *sh = demuxer->s_streams[i];
+ if (sh && sh->type == 'a') {
+ sh->ass_track = ass_mp_new_track();
+ if (sh->ass_track && sh->extradata)
+ ass_process_codec_private(sh->ass_track, sh->extradata,
+ sh->extradata_len);
+ } else if (sh && sh->type != 'v')
+ sh->ass_track = ass_mp_default_track();
}
#endif
return demuxer;
diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c
index 0ffcab0..124407a 100644
--- a/libvo/vo_gl.c
+++ b/libvo/vo_gl.c
@@ -35,8 +35,7 @@
#include "gui/interface.h"
#endif
#include "fastmemcpy.h"
-#include "libass/ass.h"
-#include "libass/ass_mp.h"
+#include "eosd.h"
static const vo_info_t info =
{
@@ -145,6 +144,7 @@ static unsigned int slice_height = 1;
static void redraw(void);
static void resize(int x,int y){
+ struct eosd_settings eosd = EOSD_SETTINGS_CLEAR;
mp_msg(MSGT_VO, MSGL_V, "[gl] Resize: %dx%d\n",x,y);
if (WinID >= 0) {
int top = 0, left = 0, w = x, h = y;
@@ -182,6 +182,22 @@ static void resize(int x,int y){
vo_osd_changed(OSDTYPE_OSD);
}
glClear(GL_COLOR_BUFFER_BIT);
+
+ if (scaled_osd) {
+ eosd.width = image_width;
+ eosd.height = image_height;
+ } else if(vo_fs) {
+ eosd.width = vo_screenwidth;
+ eosd.height = vo_screenheight;
+ eosd.margin_left = eosd.margin_right = ass_border_x;
+ eosd.margin_top = eosd.margin_bottom = ass_border_y;
+ } else {
+ eosd.width = vo_dwidth;
+ eosd.height = vo_dheight;
+ }
+ eosd.unscaled = !scaled_osd;
+ eosd_driver_configure(&eosd);
+
redraw();
}
@@ -663,6 +679,8 @@ static void do_render(void) {
}
static void do_render_osd(void) {
+ genEOSD(eosd_driver_get_images());
+ eosd_driver_release();
if (osdtexCnt > 0 || eosdDispList) {
// set special rendering parameters
if (!scaled_osd) {
@@ -1110,23 +1128,6 @@ static int control(uint32_t request, void *data, ...)
case VOCTRL_DRAW_IMAGE:
return draw_image(data);
case VOCTRL_DRAW_EOSD:
- if (!data)
- return VO_FALSE;
- genEOSD(data);
- return VO_TRUE;
- case VOCTRL_GET_EOSD_RES:
- {
- mp_eosd_res_t *r = data;
- r->mt = r->mb = r->ml = r->mr = 0;
- if (scaled_osd) {r->w = image_width; r->h = image_height;}
- else if (vo_fs) {
- r->w = vo_screenwidth; r->h = vo_screenheight;
- r->ml = r->mr = ass_border_x;
- r->mt = r->mb = ass_border_y;
- } else {
- r->w = vo_dwidth; r->h = vo_dheight;
- }
- }
return VO_TRUE;
case VOCTRL_GUISUPPORT:
return VO_TRUE;
diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c
index b06fe31..750f8bc 100644
--- a/libvo/vo_vdpau.c
+++ b/libvo/vo_vdpau.c
@@ -50,8 +50,7 @@
#include "libavutil/common.h"
-#include "libass/ass.h"
-#include "libass/ass_mp.h"
+#include "eosd.h"
static vo_info_t info = {
"VDPAU with X11",
@@ -240,6 +239,7 @@ static void resize(void)
struct vo_rect src_rect;
struct vo_rect dst_rect;
struct vo_rect borders;
+ struct eosd_settings eosd = EOSD_SETTINGS_CLEAR;
calc_src_dst_rects(vid_width, vid_height, &src_rect, &dst_rect, &borders, NULL);
out_rect_vid.x0 = dst_rect.left;
out_rect_vid.x1 = dst_rect.right;
@@ -277,6 +277,18 @@ static void resize(void)
mp_msg(MSGT_VO, MSGL_DBG2, "OUT CREATE: %u\n", output_surfaces[i]);
}
}
+
+ if (vo_fs) {
+ eosd.width = vo_screenwidth;
+ eosd.height = vo_screenheight;
+ eosd.margin_left = eosd.margin_right = border_x;
+ eosd.margin_top = eosd.margin_bottom = border_y;
+ } else {
+ eosd.width = vo_dwidth;
+ eosd.height = vo_dheight;
+ }
+ eosd_driver_configure(&eosd);
+
video_to_output_surface();
if (visible_buf)
flip_page();
@@ -732,6 +744,8 @@ static void draw_osd(void)
{
mp_msg(MSGT_VO, MSGL_DBG2, "DRAW_OSD\n");
+ generate_eosd(eosd_driver_get_images());
+ eosd_driver_release();
draw_eosd();
vo_draw_text_ext(vo_dwidth, vo_dheight, border_x, border_y, border_x, border_y,
vid_width, vid_height, draw_osd_I8A8);
@@ -1027,7 +1041,7 @@ static int control(uint32_t request, void *data, ...)
case VOCTRL_BORDER:
vo_x11_border();
resize();
- return VO_TRUE;
+ return VO_TRUE;
case VOCTRL_FULLSCREEN:
vo_x11_fullscreen();
resize();
@@ -1064,24 +1078,7 @@ static int control(uint32_t request, void *data, ...)
update_xinerama_info();
return VO_TRUE;
case VOCTRL_DRAW_EOSD:
- if (!data)
- return VO_FALSE;
- generate_eosd(data);
return VO_TRUE;
- case VOCTRL_GET_EOSD_RES: {
- mp_eosd_res_t *r = data;
- r->mt = r->mb = r->ml = r->mr = 0;
- if (vo_fs) {
- r->w = vo_screenwidth;
- r->h = vo_screenheight;
- r->ml = r->mr = border_x;
- r->mt = r->mb = border_y;
- } else {
- r->w = vo_dwidth;
- r->h = vo_dheight;
- }
- return VO_TRUE;
- }
}
return VO_NOTIMPL;
}
diff --git a/mpcommon.c b/mpcommon.c
index 1165fc4..f019f82 100644
--- a/mpcommon.c
+++ b/mpcommon.c
@@ -11,6 +11,7 @@
#include "spudec.h"
#include "version.h"
#include "vobsub.h"
+#include "eosd.h"
#ifdef CONFIG_TV_TELETEXT
#include "stream/tv.h"
#endif
@@ -145,6 +146,8 @@ void update_subtitles(sh_video_t *sh_video, demux_stream_t *d_dvdsub, int reset)
if (spudec_changed(vo_spudec))
vo_osd_changed(OSDTYPE_SPU);
} else if (dvdsub_id >= 0 && (type == 't' || type == 'm' || type == 'a')) {
+ /* The variable is called dvdsub_id, but it is about any
+ * stream-embedded subtitles. */
double curpts = sh_video->pts + sub_delay;
double endpts;
vo_sub = &subs;
@@ -176,7 +179,7 @@ void update_subtitles(sh_video_t *sh_video, demux_stream_t *d_dvdsub, int reset)
sub_add_text(&subs, packet, len, endpts);
subs.start = pts * 100;
subs.end = endpts * 100;
- ass_process_subtitle(ass_track, &subs);
+ ass_mp_process_subtitle(ass_track, &subs);
}
}
continue;
diff --git a/mplayer.c b/mplayer.c
index 1b6a350..bd97a55 100644
--- a/mplayer.c
+++ b/mplayer.c
@@ -365,6 +365,59 @@ static unsigned int initialized_flags=0;
#define mp_basename2(s) (strrchr(s,'/')==NULL?(char*)s:(strrchr(s,'/')+1))
+/* CIGAES -- ANNOYING BOUNCING BALL */
+
+#include "eosd.h"
+
+static struct eosd_client bouncing_ball_client;
+static int bouncing_ball_twidth, bouncing_ball_y;
+static ass_image_t bouncing_ball_image;
+
+static void
+bouncing_ball_configure(struct eosd_client *c, struct eosd_settings *s)
+{
+ (void)c;
+ bouncing_ball_twidth = s->width - 16;
+ bouncing_ball_y = (s->height - 16) / 2;
+}
+
+static void
+bouncing_ball_update(struct eosd_client *c, double pts)
+{
+ (void)c;
+ int x;
+
+ pts /= 8;
+ x = (pts - (int)pts) * bouncing_ball_twidth;
+ bouncing_ball_image.dst_x = x;
+ bouncing_ball_image.dst_y = bouncing_ball_y;
+ eosd_client_set_images(&bouncing_ball_client, &bouncing_ball_image, 2);
+}
+
+static void
+bouncing_ball_init(void)
+{
+ int x, y, r;
+
+ bouncing_ball_image.w = 16;
+ bouncing_ball_image.h = 16;
+ bouncing_ball_image.stride = 16;
+ bouncing_ball_image.bitmap = malloc(16 * 16);
+ bouncing_ball_image.color = 0x00FFFF00;
+ bouncing_ball_image.next = NULL;
+ for(y = 0; y < 16; y++) {
+ for(x = 0; x < 16; x++) {
+ r = 255 * ((x - 7.5) * (x - 7.5) + (y - 7.5) * (y - 7.5)) / 56.25;
+ bouncing_ball_image.bitmap[x + y * 16] = r < 256 ? 255 - r : 0;
+ }
+ }
+ bouncing_ball_client.configure = bouncing_ball_configure;
+ bouncing_ball_client.update = bouncing_ball_update;
+ eosd_client_register(&bouncing_ball_client);
+}
+
+/* CIGAES -- ANNOYING BOUNCING BALL */
+
const void *mpctx_get_video_out(MPContext *mpctx)
{
return mpctx->video_out;
@@ -704,7 +757,9 @@ void exit_player_with_rc(exit_reason_t how, int rc){
free_osd_list();
#ifdef CONFIG_ASS
- ass_library_done(ass_library);
+ current_module="uninit_ass";
+ ass_mp_renderer_done();
+ ass_mp_library_done();
#endif
current_module="exit_player";
@@ -1036,14 +1091,13 @@ void add_subtitles(char *filename, float fps, int noerr)
subd = sub_read_file(filename, fps);
#ifdef CONFIG_ASS
- if (ass_enabled)
#ifdef CONFIG_ICONV
- asst = ass_read_file(ass_library, filename, sub_cp);
+ asst = ass_mp_read_file(filename, sub_cp);
#else
- asst = ass_read_file(ass_library, filename, 0);
+ asst = ass_mp_read_file(filename, 0);
#endif
if (ass_enabled && subd && !asst)
- asst = ass_read_subdata(ass_library, subd, fps);
+ asst = ass_mp_read_subdata(subd, fps);
if (!asst && !subd)
#else
@@ -2195,8 +2249,10 @@ int reinit_video_chain(void) {
sh_video->vfilter=(void*)append_filters(sh_video->vfilter);
#ifdef CONFIG_ASS
- if (ass_enabled)
- ((vf_instance_t *)sh_video->vfilter)->control(sh_video->vfilter, VFCTRL_INIT_EOSD, ass_library);
+ if (ass_enabled) {
+ ((vf_instance_t *)sh_video->vfilter)->control(sh_video->vfilter, VFCTRL_INIT_EOSD, NULL);
+ ass_mp_renderer_init();
+ }
#endif
current_module="init_video_codec";
@@ -2514,6 +2570,7 @@ int i;
int gui_no_filename=0;
+/* CIGAES -- ANNOYING BOUNCING BALL */ bouncing_ball_init();
InitTimer();
srand(GetTimerMS());
@@ -2769,7 +2826,7 @@ if(!codecs_file || !parse_codec_cfg(codecs_file)){
vo_init_osd();
#ifdef CONFIG_ASS
- ass_library = ass_init();
+ ass_mp_library_init();
#endif
#ifdef HAVE_RTC
@@ -3278,15 +3335,13 @@ if (mpctx->global_sub_size <= mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + dvds
mpctx->global_sub_size = mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + dvdsub_id + 1;
#ifdef CONFIG_ASS
-if (ass_enabled && ass_library) {
- for (i = 0; i < mpctx->demuxer->num_attachments; ++i) {
- demux_attachment_t* att = mpctx->demuxer->attachments + i;
- if (extract_embedded_fonts &&
- att->name && att->type && att->data && att->data_size &&
- (strcmp(att->type, "application/x-truetype-font") == 0 ||
- strcmp(att->type, "application/x-font") == 0))
- ass_add_font(ass_library, att->name, att->data, att->data_size);
- }
+for (i = 0; i < mpctx->demuxer->num_attachments; ++i) {
+ demux_attachment_t* att = mpctx->demuxer->attachments + i;
+ if (extract_embedded_fonts &&
+ att->name && att->type && att->data && att->data_size &&
+ (strcmp(att->type, "application/x-truetype-font") == 0 ||
+ strcmp(att->type, "application/x-font") == 0))
+ ass_mp_add_font(att->name, att->data, att->data_size);
}
#endif
@@ -3945,8 +4000,7 @@ vo_sub_last = vo_sub=NULL;
subdata=NULL;
#ifdef CONFIG_ASS
ass_track = NULL;
-if(ass_library)
- ass_clear_fonts(ass_library);
+ass_mp_clear_fonts();
#endif
if(mpctx->eof == PT_NEXT_ENTRY || mpctx->eof == PT_PREV_ENTRY) {
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: Digital signature
URL: <http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/attachments/20090226/17a1d587/attachment.pgp>
More information about the MPlayer-dev-eng
mailing list