[MPlayer-dev-eng] [PATCH] Support for DVB subtitles

Nicolas George nicolas.george at normalesup.org
Sat Feb 28 16:11:20 CET 2009


Le decadi 10 ventôse, an CCXVII, Diego Biurrun a écrit :
> nit: one more occasion:

Done.

> But don't bother sending an update just for this, I bet there will be
> more comments.

There was a small endianness mistake in the palette handling anyway.
Corrected version attached.

Regards,

-- 
  Nicolas George
-------------- next part --------------
 Makefile                |    1 +
 command.c               |    4 +
 dvbsub.c                |  238 +++++++++++++++++++++++++++++++++++++++++++++++
 dvbsub.h                |   40 ++++++++
 libmpcodecs/vf_ass.c    |   12 ++-
 libmpdemux/demux_lavf.c |    3 +
 libmpdemux/demuxer.c    |   14 +++-
 libmpdemux/stheader.h   |    3 +
 mp_msg.h                |    2 +
 mpcommon.c              |   12 ++-
 10 files changed, 320 insertions(+), 9 deletions(-)

diff --git a/Makefile b/Makefile
index 449b058..145a31c 100644
--- a/Makefile
+++ b/Makefile
@@ -36,6 +36,7 @@ LDFLAGS_MENCODER = $(EXTRALIBS_MENCODER) \
 SRCS_COMMON = asxparser.c \
               codec-cfg.c \
               cpudetect.c \
+              dvbsub.c \
               edl.c \
               find_sub.c \
               fmt-conversion.c \
diff --git a/command.c b/command.c
index 0a67038..7d571d9 100644
--- a/command.c
+++ b/command.c
@@ -27,6 +27,7 @@
 #include "libmpcodecs/dec_video.h"
 #include "vobsub.h"
 #include "spudec.h"
+#include "dvbsub.h"
 #include "get_path.h"
 #ifdef CONFIG_TV
 #include "stream/tv.h"
@@ -1455,6 +1456,7 @@ static int mp_property_sub(m_option_t * prop, int action, void *arg,
 #ifdef CONFIG_ASS
     ass_track = 0;
 #endif
+    dvbsub_current = NULL;
 
     if (source == SUB_SOURCE_VOBSUB) {
         vobsub_id = vobsub_get_id_by_index(vo_vobsub, mpctx->global_sub_pos - mpctx->global_sub_indices[SUB_SOURCE_VOBSUB]);
@@ -1491,6 +1493,8 @@ static int mp_property_sub(m_option_t * prop, int action, void *arg,
 		sh_sub_t *sh = d_sub->sh;
 		if (sh->type == 'v')
 		    init_vo_spudec();
+		else if(sh->type == 'b')
+		    dvbsub_current = sh->dvbsub_track;
 #ifdef CONFIG_ASS
 		else if (ass_enabled)
 		    ass_track = sh->ass_track;
diff --git a/dvbsub.c b/dvbsub.c
new file mode 100644
index 0000000..5ea5f36
--- /dev/null
+++ b/dvbsub.c
@@ -0,0 +1,238 @@
+/*
+ * DVB subtitles
+ *
+ * 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 "mplayer.h"
+#include "mp_msg.h"
+#include "libavcodec/avcodec.h"
+#include "dvbsub.h"
+
+struct dvbsub_track {
+    struct dvbsub_frame *frames;
+    AVCodecContext *decoder;
+    ass_image_t *images;
+    int frame_w;
+    int frame_h;
+    int rendered;
+};
+
+struct dvbsub_frame {
+    struct dvbsub_frame *next;
+    AVSubtitleRect **rect;
+    float start, end;
+    int n_rect;
+};
+
+struct dvbsub_track *dvbsub_current = NULL;
+
+static void dvbsub_invalidate_images(struct dvbsub_track *track)
+{
+    ass_image_t *ni;
+
+    track->rendered = 0;
+    while (track->images) {
+        ni = track->images->next;
+        free(track->images->bitmap);
+        free(track->images);
+        track->images = ni;
+    }
+}
+
+static void dvbsub_free_frame(struct dvbsub_frame *frame)
+{
+    int i;
+
+    for (i = 0; i < frame->n_rect; i++) {
+        av_free(frame->rect[i]->pict.data[0]);
+        av_free(frame->rect[i]->pict.data[1]);
+        av_free(frame->rect[i]);
+    }
+    av_free(frame->rect);
+    av_free(frame);
+}
+
+struct dvbsub_track *dvbsub_create(int sub_id)
+{
+    struct dvbsub_track *track;
+    int r;
+    AVCodec *codec;
+
+    track = malloc(sizeof(struct dvbsub_track));
+    if (track == NULL)
+        return NULL;
+    track->decoder = avcodec_alloc_context();
+    codec = avcodec_find_decoder(CODEC_ID_DVB_SUBTITLE);
+    if (codec == NULL) {
+        mp_msg(MSGT_DVBSUB, MSGL_ERR, "dvbsub decoder not found\n");
+        free(track);
+        return NULL;
+    }
+    track->decoder->sub_id = sub_id;
+    r = avcodec_open(track->decoder, codec);
+    if (r < 0) {
+        mp_msg(MSGT_DVBSUB, MSGL_ERR, "dvbsub decoder not opened\n");
+        av_free(track->decoder);
+        free(track);
+        return NULL;
+    }
+    track->frames   = NULL;
+    track->images   = NULL;
+    track->rendered = 0;
+    track->frame_w  = 720;
+    track->frame_h  = 576;
+    return track;
+}
+
+void dvbsub_destroy(struct dvbsub_track *track)
+{
+    struct dvbsub_frame *nf;
+
+    dvbsub_invalidate_images(track);
+    while (track->frames) {
+        nf = track->frames->next;
+        dvbsub_free_frame(track->frames);
+        track->frames = nf;
+    }
+}
+
+static void dvbsub_add_frame(struct dvbsub_track *track,
+                             double start, double end,
+                             AVSubtitleRect **rect, int n_rect)
+{
+    struct dvbsub_frame *frame, *f, **prev, *nf;
+
+    frame = malloc(sizeof(struct dvbsub_frame));
+    if (frame == NULL)
+        return;
+    frame->start  = start;
+    frame->end    = end;
+    frame->rect   = rect;
+    frame->n_rect = n_rect;
+
+    f = track->frames;
+    prev = &track->frames;
+    for (f = track->frames; f; f = f->next) {
+        if (f->start >= start)
+            break;
+        if (f->end >= start)
+            f->end = start;
+        prev = &f->next;
+    }
+    *prev = frame;
+    frame->next = NULL;
+    while (f) {
+        nf = f->next;
+        dvbsub_free_frame(f);
+        f = nf;
+    }
+    dvbsub_invalidate_images(track);
+}
+
+void dvbsub_process_packet(struct dvbsub_track *track,
+                           uint8_t *packet, int size, double pts)
+{
+    int r, got_sub = 0;
+    AVSubtitle sub;
+
+    r = avcodec_decode_subtitle(track->decoder, &sub, &got_sub, packet, size);
+    if (!got_sub)
+        return;
+    if (sub.format != 0)
+        return;
+    dvbsub_add_frame(track, pts + sub.start_display_time / 1000.0,
+        pts + sub.end_display_time / 1000.0, sub.rects, sub.num_rects);
+}
+
+ass_image_t *dvbsub_render_frame(struct dvbsub_track *track, double pts,
+                                 int frame_w, int frame_h)
+{
+    struct dvbsub_frame *frame, *nf;
+    ass_image_t *ni, **tail;
+    int i, psize, x, y, w, h, dx, dy, stride;
+    uint32_t *palette;
+    uint8_t *src, *dst;
+
+    if (track->frame_w != frame_w || track->frame_h != frame_h) {
+        dvbsub_invalidate_images(track);
+        track->frame_w = frame_w;
+        track->frame_h = frame_h;
+    }
+    pts += sub_delay;
+    while (track->frames && track->frames->end < pts) {
+        nf = track->frames->next;
+        dvbsub_free_frame(track->frames);
+        dvbsub_invalidate_images(track);
+        track->frames = nf;
+    }
+    if (track->rendered)
+        return track->images;
+    frame = track->frames;
+    tail = &track->images;
+    if (frame) {
+        for (i = 0; i < frame->n_rect; i++) {
+            palette = (uint32_t *)frame->rect[i]->pict.data[1];
+            psize = frame->rect[i]->nb_colors;
+            x = frame->rect[i]->x;
+            y = frame->rect[i]->y;
+            w = frame->rect[i]->w;
+            h = frame->rect[i]->h;
+            dx = 0;
+            dy = 0;
+            if (x < 0) {
+                w += x;
+                dx -= x;
+                x = 0;
+            }
+            if (x + w > frame_w)
+                w = frame_w - x;
+            if (y < 0) {
+                h -= y;
+                dy -= y;
+                y = 0;
+            }
+            if (y + h > track->frame_h)
+                h = track->frame_h - y;
+            if (w < 0 || h < 0)
+                continue;
+            stride = frame->rect[i]->pict.linesize[0];
+            src = frame->rect[i]->pict.data[0] + dx + dy * stride;
+            ni = malloc(sizeof(ass_image_t));
+            ni->w =      w;
+            ni->h =      h;
+            ni->dst_x =  x;
+            ni->dst_y =  y;
+            ni->stride = w;
+            ni->bitmap = malloc(w * h);
+            dst = ni->bitmap;
+            for (y = 0; y < h; y++) {
+                for (x = 0; x < w; x++)
+                    *(dst++) = palette[*(src++)] >> 24;
+                src += stride - w;
+            }
+            ni->color = (palette[psize - 1] & 0xFFFFFF) << 8;
+            *tail = ni;
+            tail = &ni->next;
+        }
+    }
+    *tail = NULL;
+    track->rendered = 1;
+    return track->images;
+}
diff --git a/dvbsub.h b/dvbsub.h
new file mode 100644
index 0000000..f2e0c5c
--- /dev/null
+++ b/dvbsub.h
@@ -0,0 +1,40 @@
+/*
+ * DVB subtitles
+ *
+ * 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_DVBSUB_H
+#define MPLAYER_DVBSUB_H
+
+#include <stdint.h>
+#include "libass/ass.h"
+
+struct dvbsub_track;
+
+extern struct dvbsub_track *dvbsub_current;
+
+struct dvbsub_track *dvbsub_create(int sub_id);
+void dvbsub_destroy(struct dvbsub_track *track);
+void dvbsub_process_packet(struct dvbsub_track *track,
+                           uint8_t *packet, int size, double pts);
+ass_image_t *dvbsub_render_frame(struct dvbsub_track *track, double pts,
+                                 int frame_w, int frame_h);
+
+#endif /* MPLAYER_DVBSUB_H */
diff --git a/libmpcodecs/vf_ass.c b/libmpcodecs/vf_ass.c
index f33aba4..2513481 100644
--- a/libmpcodecs/vf_ass.c
+++ b/libmpcodecs/vf_ass.c
@@ -41,6 +41,8 @@
 #include "m_option.h"
 #include "m_struct.h"
 
+#include "dvbsub.h"
+
 #include "libass/ass.h"
 #include "libass/ass_mp.h"
 
@@ -327,9 +329,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);
-	
+	if (sub_visibility && (pts != MP_NOPTS_VALUE)) {
+		if (dvbsub_current)
+			images = dvbsub_render_frame(dvbsub_current, pts,
+				vf->priv->outw, vf->priv->outh);
+		else if (vf->priv->ass_priv && ass_track)
+			images = ass_mp_render_frame(vf->priv->ass_priv, ass_track, (pts+sub_delay) * 1000 + .5, NULL);
+	}
 	prepare_image(vf, mpi);
 	if (images) render_frame(vf, mpi, images);
 
diff --git a/libmpdemux/demux_lavf.c b/libmpdemux/demux_lavf.c
index 9749054..ffe04d1 100644
--- a/libmpdemux/demux_lavf.c
+++ b/libmpdemux/demux_lavf.c
@@ -386,6 +386,8 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i) {
                 type = 'a';
             else if(codec->codec_id == CODEC_ID_DVD_SUBTITLE)
                 type = 'v';
+            else if(codec->codec_id == CODEC_ID_DVB_SUBTITLE)
+                type = 'b';
             else
                 break;
             sh_sub = new_sh_sub_sid(demuxer, i, priv->sub_streams);
@@ -398,6 +400,7 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i) {
                 memcpy(sh_sub->extradata, codec->extradata, codec->extradata_size);
                 sh_sub->extradata_len = codec->extradata_size;
             }
+            sh_sub->sub_id = codec->sub_id;
             if (st->language)
               sh_sub->lang = strdup(st->language);
             if (st->disposition & AV_DISPOSITION_DEFAULT)
diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c
index 74bbd8e..206a93c 100644
--- a/libmpdemux/demuxer.c
+++ b/libmpdemux/demuxer.c
@@ -22,6 +22,7 @@
 
 #include "libaf/af_format.h"
 
+#include "dvbsub.h"
 #ifdef CONFIG_ASS
 #include "libass/ass.h"
 #include "libass/ass_mp.h"
@@ -272,6 +273,8 @@ void free_sh_sub(sh_sub_t *sh)
     if (sh->ass_track)
         ass_free_track(sh->ass_track);
 #endif
+    if (sh->dvbsub_track)
+        dvbsub_destroy(sh->dvbsub_track);
     free(sh->lang);
     free(sh);
 }
@@ -914,13 +917,20 @@ static demuxer_t *demux_open_stream(stream_t *stream, int file_format,
     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') {
+            if (!sh)
+                continue;
+            if (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')
+            } else if (sh->type == 'v') {
+                /* nothing */
+            } else if (sh->type == 'b') {
+                sh->dvbsub_track = dvbsub_create(sh->sub_id);
+            } else {
                 sh->ass_track = ass_default_track(ass_library);
+            }
         }
     }
 #endif
diff --git a/libmpdemux/stheader.h b/libmpdemux/stheader.h
index 07ecfae..178aeef 100644
--- a/libmpdemux/stheader.h
+++ b/libmpdemux/stheader.h
@@ -4,6 +4,7 @@
 #include "demuxer.h"
 #include "aviheader.h"
 #include "ms_hdr.h"
+#include "dvbsub.h"
 
 // Stream headers:
 
@@ -99,9 +100,11 @@ typedef struct {
   char type;                    // t = text, v = VobSub, a = SSA/ASS
   unsigned char* extradata; // extra header data passed from demuxer
   int extradata_len;
+  int sub_id; // sub_id passed from demuxer
 #ifdef CONFIG_ASS
   ass_track_t* ass_track;  // for SSA/ASS streams (type == 'a')
 #endif
+  struct dvbsub_track *dvbsub_track;
   char* lang; // track language
   int default_track;
 } sh_sub_t;
diff --git a/mp_msg.h b/mp_msg.h
index 0f2776f..ee6919d 100644
--- a/mp_msg.h
+++ b/mp_msg.h
@@ -104,6 +104,8 @@ extern int verbose;
 
 #define MSGT_STATUSLINE 45 // playback/encoding status line
 
+#define MSGT_DVBSUB 46 // DVB subtitles
+
 #define MSGT_MAX 64
 
 void mp_msg_init(void);
diff --git a/mpcommon.c b/mpcommon.c
index 1165fc4..0c8646c 100644
--- a/mpcommon.c
+++ b/mpcommon.c
@@ -11,6 +11,7 @@
 #include "spudec.h"
 #include "version.h"
 #include "vobsub.h"
+#include "dvbsub.h"
 #ifdef CONFIG_TV_TELETEXT
 #include "stream/tv.h"
 #endif
@@ -144,7 +145,7 @@ 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')) {
+    } else if (dvdsub_id >= 0 && (type == 't' || type == 'm' || type == 'a' || type == 'b')) {
         double curpts = sh_video->pts + sub_delay;
         double endpts;
         vo_sub = &subs;
@@ -163,12 +164,15 @@ void update_subtitles(sh_video_t *sh_video, demux_stream_t *d_dvdsub, int reset)
             if (ass_enabled) {
                 sh_sub_t* sh = d_dvdsub->sh;
                 ass_track = sh ? sh->ass_track : NULL;
-                if (!ass_track) continue;
-                if (type == 'a') { // ssa/ass subs with libass
+                if (ass_track && type == 'a') { // ssa/ass subs with libass
                     ass_process_chunk(ass_track, packet, len,
                                       (long long)(pts*1000 + 0.5),
                                       (long long)((endpts-pts)*1000 + 0.5));
-                } else { // plaintext subs with libass
+                } else if (type == 'b') { // DVB subs
+                    if (sh && sh->dvbsub_track)
+                        dvbsub_process_packet(sh->dvbsub_track,
+                            packet, len, pts);
+                } else if (ass_track) { // plaintext subs with libass
                     vo_sub = NULL;
                     if (pts != MP_NOPTS_VALUE) {
                         if (endpts == MP_NOPTS_VALUE) endpts = pts + 3;
-------------- 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/20090228/ddb132f9/attachment.pgp>


More information about the MPlayer-dev-eng mailing list