Enable ASS/SSA subtitle support in mencoder
authorgreg <greg@b3059339-0415-0410-9bf9-f77b7e298cf2>
Fri, 5 Mar 2010 23:13:08 +0000 (23:13 +0000)
committergreg <greg@b3059339-0415-0410-9bf9-f77b7e298cf2>
Fri, 5 Mar 2010 23:13:08 +0000 (23:13 +0000)
Enable ASS/SSA rendering through libass in mencoder. This duplicates a
bit of code (to parse font attachments, for example). Additionally, add
a filter "fixpts" that generates PTS, simulating fixed fps. PTS
generated by this filter are then used for subtitle timing.

Original patch by Nicolas George. (nicolas.george normalesup.org)

git-svn-id: svn://git.mplayerhq.hu/mplayer/trunk@30849 b3059339-0415-0410-9bf9-f77b7e298cf2

DOCS/man/en/mplayer.1
Makefile
libmpcodecs/vf.c
libmpcodecs/vf_fixpts.c [new file with mode: 0644]
mencoder.c

index cf89c3b..e984b4b 100644 (file)
@@ -7500,6 +7500,48 @@ Larger radius makes for smoother gradients, but also prevents the filter
 from modifying pixels near detailed regions (default: 16).
 .RE
 .
+.TP
+.B fixpts[=options]
+Fixes the presentation timestamps (PTS) of the frames.
+By default, the PTS passed to the next filter is dropped, but the following
+options can change that:
+.RSs
+.IPs print
+Print the incoming PTS.
+.IPs fps=<fps>
+Specify a frame per second value.
+.IPs start=<pts>
+Specify an initial value for the PTS.
+.IPs autostart=<n>
+Uses the
+.IR n th
+incoming PTS as the initial PTS.
+All previous pts are kept, so setting a huge value or \-1 keeps the PTS
+intact.
+.IPs autofps=<n>
+Uses the
+.IR n th
+incoming PTS after the end of autostart to determine the framerate.
+.RE
+.sp 1
+.RS
+.I EXAMPLE:
+.RE
+.PD 0
+.RSs
+.IPs "\-vf fixpts=fps=24000/1001,ass,fixpts"
+Generates a new sequence of PTS, uses it for ASS subtitles, then drops it.
+Generating a new sequence is useful when the timestamps are reset during the
+program; this is frequent on DVDs.
+Dropping it may be necessary to avoid confusing encoders.
+.RE
+.PD 1
+.sp 1
+.RS
+.I NOTE:
+Using this filter together with any sort of seeking (including -ss and EDLs)
+may make demons fly out of your nose.
+.RE
 .
 .
 .SH "GENERAL ENCODING OPTIONS (MENCODER ONLY)"
index 82f6331..b66562b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -430,6 +430,7 @@ SRCS_COMMON = asxparser.c \
               libmpcodecs/vf_field.c \
               libmpcodecs/vf_fil.c \
               libmpcodecs/vf_filmdint.c \
+              libmpcodecs/vf_fixpts.c \
               libmpcodecs/vf_flip.c \
               libmpcodecs/vf_format.c \
               libmpcodecs/vf_framestep.c \
index d98b11a..02b0f1a 100644 (file)
@@ -118,6 +118,7 @@ extern const vf_info_t vf_info_yadif;
 extern const vf_info_t vf_info_blackframe;
 extern const vf_info_t vf_info_geq;
 extern const vf_info_t vf_info_ow;
+extern const vf_info_t vf_info_fixpts;
 
 // list of available filters:
 static const vf_info_t* const filter_list[]={
@@ -211,6 +212,7 @@ static const vf_info_t* const filter_list[]={
     &vf_info_yadif,
     &vf_info_blackframe,
     &vf_info_ow,
+    &vf_info_fixpts,
     NULL
 };
 
diff --git a/libmpcodecs/vf_fixpts.c b/libmpcodecs/vf_fixpts.c
new file mode 100644 (file)
index 0000000..5ceb516
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "help_mp.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+struct vf_priv_s {
+    double current;
+    double step;
+    int autostart;
+    int autostep;
+    unsigned have_step:1;
+    unsigned print:1;
+};
+
+static int put_image(vf_instance_t *vf, mp_image_t *src, double pts)
+{
+    struct vf_priv_s *p = vf->priv;
+
+    if (p->print) {
+        if (pts == MP_NOPTS_VALUE)
+            mp_msg(MSGT_VFILTER, MSGL_INFO, "PTS: undef\n");
+        else
+            mp_msg(MSGT_VFILTER, MSGL_INFO, "PTS: %f\n", pts);
+    }
+    if (pts != MP_NOPTS_VALUE && p->autostart != 0) {
+        p->current = pts;
+        if (p->autostart > 0)
+            p->autostart--;
+    } else if (pts != MP_NOPTS_VALUE && p->autostep > 0) {
+        p->step = pts - p->current;
+        p->current = pts;
+        p->autostep--;
+        p->have_step = 1;
+    } else if (p->have_step) {
+        p->current += p->step;
+        pts = p->current;
+    } else {
+        pts = MP_NOPTS_VALUE;
+    }
+    return vf_next_put_image(vf, src, pts);
+}
+
+static void uninit(vf_instance_t *vf)
+{
+    free(vf->priv);
+}
+
+static int parse_args(struct vf_priv_s *p, const char *args)
+{
+    int pos;
+    double num, denom = 1;
+    int iarg;
+
+    while (*args != 0) {
+        pos = 0;
+        if (sscanf(args, "print%n", &pos) == 0 && pos > 0) {
+            p->print = 1;
+        } else if (sscanf(args, "fps=%lf%n/%lf%n", &num, &pos, &denom, &pos) >=
+                   1 && pos > 0) {
+            p->step = denom / num;
+            p->have_step = 1;
+        } else if (sscanf(args, "start=%lf%n", &num, &pos) >= 1 && pos > 0) {
+            p->current = num;
+        } else if (sscanf(args, "autostart=%d%n", &iarg, &pos) == 1 && pos > 0) {
+            p->autostart = iarg;
+        } else if (sscanf(args, "autofps=%d%n", &iarg, &pos) == 1 && pos > 0) {
+            p->autostep = iarg;
+        } else {
+            mp_msg(MSGT_VFILTER, MSGL_FATAL,
+                   "fixpts: unknown suboption: %s\n", args);
+            return 0;
+        }
+        args += pos;
+        if (*args == ':')
+            args++;
+    }
+    return 1;
+}
+
+static int open(vf_instance_t *vf, char *args)
+{
+    struct vf_priv_s *p;
+    struct vf_priv_s ptmp = {
+        .current = 0,
+        .step = 0,
+        .autostart = 0,
+        .autostep = 0,
+        .have_step = 0,
+        .print = 0,
+    };
+
+    if (!parse_args(&ptmp, args == NULL ? "" : args))
+        return 0;
+
+    vf->put_image = put_image;
+    vf->uninit = uninit;
+    vf->priv = p = malloc(sizeof(struct vf_priv_s));
+    *p = ptmp;
+    p->current = -p->step;
+
+    return 1;
+}
+
+vf_info_t vf_info_fixpts = {
+    "Fix presentation timestamps",
+    "fixpts",
+    "Nicolas George",
+    "",
+    &open,
+    NULL
+};
index ce0a6c3..7b9f570 100644 (file)
@@ -370,6 +370,46 @@ static void exit_sighandler(int x){
 
 static muxer_t* muxer=NULL;
 
+void add_subtitles(char *filename, float fps, int silent)
+{
+    sub_data *subd;
+#ifdef CONFIG_ASS
+    ass_track_t *asst = 0;
+#endif
+
+    if (!filename) return;
+
+    subd = sub_read_file(filename, fps);
+#ifdef CONFIG_ASS
+    if (ass_enabled)
+#ifdef CONFIG_ICONV
+        asst = ass_read_file(ass_library, filename, sub_cp);
+#else
+        asst = ass_read_file(ass_library, filename, 0);
+#endif
+    if (ass_enabled && subd && !asst)
+        asst = ass_read_subdata(ass_library, subd, fps);
+
+    if (!asst && !subd && !silent)
+#else
+    if (!subd && !silent)
+#endif
+        mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_CantLoadSub,
+                   filename_recode(filename));
+
+#ifdef CONFIG_ASS
+    if (!asst && !subd) return;
+    ass_track = asst;
+#else
+    if (!subd) return;
+#endif
+    mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_FILE_SUB_FILENAME=%s\n",
+          filename_recode(filename));
+    subdata = subd;
+}
+
+void print_wave_header(WAVEFORMATEX *h, int verbose_level);
+
 int main(int argc,char* argv[]){
 
 stream_t* stream=NULL;
@@ -527,6 +567,10 @@ play_next_file:
   m_entry_set_options(mconfig,&filelist[curfile]);
   filename = filelist[curfile].name;
 
+#ifdef CONFIG_ASS
+  ass_library = ass_init();
+#endif
+
   if(!filename){
        mp_msg(MSGT_CPLAYER, MSGL_FATAL, MSGTR_MissingFilename);
        mencoder_exit(1,NULL);
@@ -658,26 +702,6 @@ if(sh_audio && (out_audio_codec || seek_to_sec || !sh_audio->wf || playback_spee
     }
   }
 
-// after reading video params we should load subtitles because
-// we know fps so now we can adjust subtitles time to ~6 seconds AST
-// check .sub
-//  current_module="read_subtitles_file";
-  if(sub_name && sub_name[0]){
-    subdata=sub_read_file(sub_name[0], sh_video->fps);
-    if(!subdata) mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_CantLoadSub,sub_name[0]);
-  } else
-  if(sub_auto && filename) { // auto load sub file ...
-    char **tmp = NULL;
-    int i = 0;
-    char *psub = get_path( "sub/" );
-    tmp = sub_filenames((psub ? psub : ""), filename);
-    free(psub);
-    subdata=sub_read_file(tmp[0], sh_video->fps);
-    while (tmp[i])
-      free(tmp[i++]);
-    free(tmp);
-  }
-
 // set up video encoder:
 
 if (!curfile) { // curfile is non zero when a second file is opened
@@ -857,12 +881,77 @@ default: {
     ve = sh_video->vfilter;
   } else sh_video->vfilter = ve;
     // append 'expand' filter, it fixes stride problems and renders osd:
+#ifdef CONFIG_ASS
+    if (auto_expand && !ass_enabled) { /* we do not want both */
+#else
     if (auto_expand) {
+#endif
       char* vf_args[] = { "osd", "1", NULL };
       sh_video->vfilter=vf_open_filter(sh_video->vfilter,"expand",vf_args);
     }
+
+#ifdef CONFIG_ASS
+  if(ass_enabled) {
+    int i;
+    int insert = 1;
+    if (vf_settings)
+      for (i = 0; vf_settings[i].name; ++i)
+        if (strcmp(vf_settings[i].name, "ass") == 0) {
+          insert = 0;
+          break;
+        }
+    if (insert) {
+      extern vf_info_t vf_info_ass;
+      vf_info_t* libass_vfs[] = {&vf_info_ass, NULL};
+      char* vf_arg[] = {"auto", "1", NULL};
+      vf_instance_t* vf_ass = vf_open_plugin(libass_vfs,sh_video->vfilter,"ass",vf_arg);
+      if (vf_ass)
+        sh_video->vfilter=(void*)vf_ass;
+      else
+        mp_msg(MSGT_CPLAYER,MSGL_ERR, "ASS: cannot add video filter\n");
+    }
+
+    if (ass_library) {
+      for (i = 0; i < demuxer->num_attachments; ++i) {
+        demux_attachment_t* att = 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);
+      }
+    }
+  }
+#endif
+
     sh_video->vfilter=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);
+#endif
+
+// after reading video params we should load subtitles because
+// we know fps so now we can adjust subtitles time to ~6 seconds AST
+// check .sub
+  if(sub_name && sub_name[0]){
+    for (i = 0; sub_name[i] != NULL; ++i)
+        add_subtitles (sub_name[i], sh_video->fps, 0);
+  } else
+  if(sub_auto && filename) { // auto load sub file ...
+    char **tmp = NULL;
+    int i = 0;
+    char *psub = get_path( "sub/" );
+    tmp = sub_filenames((psub ? psub : ""), filename);
+    free(psub);
+    while (tmp[i])
+    {
+      add_subtitles (tmp[i], sh_video->fps, 0);
+      free(tmp[i++]);
+    }
+    free(tmp);
+  }
+
     mp_msg(MSGT_CPLAYER,MSGL_INFO,"==========================================================================\n");
     init_best_video_codec(sh_video,video_codec_list,video_fm_list);
     mp_msg(MSGT_CPLAYER,MSGL_INFO,"==========================================================================\n");