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

Nicolas George nicolas.george at normalesup.org
Sat Feb 28 00:41:53 CET 2009


Hi.

The attached patch adds support for DVB subtitles. The color is currently
downgraded to a single alpha channel; there is room for enhancement here.

If anyone thinks that the way I plugged it directly in vf_ass, my answer is
that I totally agree and I would like some help to have my previous patch
accepted.

Regards,

-- 
  Nicolas George
-------------- next part --------------
 Makefile                |    1 +
 command.c               |    4 +
 dvbsub.c                |  208 +++++++++++++++++++++++++++++++++++++++++++++++
 dvbsub.h                |   17 ++++
 libmpcodecs/vf_ass.c    |   12 ++-
 libmpdemux/demux_lavf.c |    6 ++
 mp_msg.h                |    2 +
 mpcommon.c              |    6 +-
 8 files changed, 251 insertions(+), 5 deletions(-)

diff --git a/Makefile b/Makefile
index 449b058..67e8315 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..02c941f 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_reset();
 
     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_init(sh->extradata, sh->extradata_len);
 #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..c5fcd3e
--- /dev/null
+++ b/dvbsub.c
@@ -0,0 +1,208 @@
+#include "mplayer.h"
+#include "mp_msg.h"
+#include "libavcodec/avcodec.h"
+#include "dvbsub.h"
+
+struct dvbsus_frame {
+    dvbsub_frame_t *next;
+    AVSubtitleRect **rect;
+    float start, end;
+    int n_rect;
+};
+
+static AVCodecContext *decoder = NULL;
+dvbsub_frame_t *dvbsub_frames = NULL;
+static ass_image_t *images = NULL;
+static int rendered = 0;
+static int frame_w = 360;
+static int frame_h = 576;
+
+static void dvbsub_invalidate_images(void)
+{
+    ass_image_t *ni;
+
+    rendered = 0;
+    while (images) {
+        ni = images->next;
+        free(images->bitmap);
+        free(images);
+        images = ni;
+    }
+}
+
+void dvbsub_configure(int w, int h)
+{
+    dvbsub_invalidate_images();
+    frame_w = w;
+    frame_h = h;
+}
+
+static void dvbsub_free_frame(dvbsub_frame_t *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);
+    dvbsub_invalidate_images();
+}
+
+void dvbsub_reset(void)
+{
+    dvbsub_frame_t *nf;
+
+    while (dvbsub_frames) {
+        nf = dvbsub_frames->next;
+        dvbsub_free_frame(dvbsub_frames);
+        dvbsub_frames = nf;
+    }
+    dvbsub_invalidate_images();
+    if (decoder) {
+        avcodec_close(decoder);
+        av_freep(&decoder);
+    }
+}
+
+void dvbsub_init(void *extradata, int extradata_size)
+{
+    int r;
+    AVCodec *codec;
+
+    dvbsub_reset();
+    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");
+        return;
+    }
+    if (extradata_size == sizeof(int))
+        decoder->sub_id = *(int *)extradata;
+    r = avcodec_open(decoder, codec);
+    if (r < 0) {
+        mp_msg(MSGT_DVBSUB, MSGL_ERR, "dvbsub decoder not opened\n");
+        av_freep(&decoder);
+        return;
+    }
+}
+
+static void dvbsub_add_frame(double start, double end, AVSubtitleRect **rect,
+    int n_rect)
+{
+    dvbsub_frame_t *frame, *f, **prev, *nf;
+
+    frame = malloc(sizeof(dvbsub_frame_t));
+    if (frame == NULL)
+        return;
+    frame->start = start;
+    frame->end = end;
+    frame->rect = rect;
+    frame->n_rect = n_rect;
+
+    f = dvbsub_frames;
+    prev = &dvbsub_frames;
+    for (f = dvbsub_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();
+}
+
+void dvbsub_process_packet(uint8_t *packet, int size, double pts)
+{
+    int r, got_sub = 0;
+    AVSubtitle sub;
+
+    if (!decoder)
+        return;
+    r = avcodec_decode_subtitle(decoder, &sub, &got_sub, packet, size);
+    if (!got_sub)
+        return;
+    if (sub.format != 0)
+        return;
+    dvbsub_add_frame(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(double pts)
+{
+    dvbsub_frame_t *nf;
+    ass_image_t *ni, **tail;
+    int i, psize, x, y, w, h, dx, dy, stride;
+    uint8_t (*palette)[4];
+    uint8_t *src, *dst;
+
+    pts += sub_delay;
+    while (dvbsub_frames && dvbsub_frames->end < pts) {
+        nf = dvbsub_frames->next;
+        dvbsub_free_frame(dvbsub_frames);
+        dvbsub_frames = nf;
+    }
+    if (rendered)
+        return images;
+    tail = &images;
+    if (dvbsub_frames) {
+        for (i = 0; i < dvbsub_frames->n_rect; i++) {
+            palette = (uint8_t (*)[4])dvbsub_frames->rect[i]->pict.data[1];
+            psize = dvbsub_frames->rect[i]->nb_colors;
+            x = dvbsub_frames->rect[i]->x;
+            y = dvbsub_frames->rect[i]->y;
+            w = dvbsub_frames->rect[i]->w;
+            h = dvbsub_frames->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 > frame_h)
+                h = frame_h - y;
+            if (w < 0 || h < 0)
+                continue;
+            stride = dvbsub_frames->rect[i]->pict.linesize[0];
+            src = dvbsub_frames->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++)][3];
+                src += stride - w;
+            }
+            ni->color = palette[psize - 1][0] * 0x100 +
+                palette[psize - 1][1] * 0x10000 +
+                palette[psize - 1][2] * 0x1000000;
+            *tail = ni;
+            tail = &ni->next;
+        }
+    }
+    *tail = NULL;
+    rendered = 1;
+    return images;
+}
diff --git a/dvbsub.h b/dvbsub.h
new file mode 100644
index 0000000..7049904
--- /dev/null
+++ b/dvbsub.h
@@ -0,0 +1,17 @@
+#ifndef MPLAYER_DVBSUB_H
+#define MPLAYER_DVBSUB_H
+
+#include <stdint.h>
+#include "libass/ass.h"
+
+typedef struct dvbsus_frame dvbsub_frame_t;
+
+extern dvbsub_frame_t *dvbsub_frames;
+
+void dvbsub_configure(int w, int h);
+void dvbsub_init(void *extradata, int extradata_size);
+void dvbsub_reset(void);
+void dvbsub_process_packet(uint8_t *packet, int size, double pts);
+ass_image_t *dvbsub_render_frame(double pts);
+
+#endif /* MPLAYER_DVBSUB_H */
diff --git a/libmpcodecs/vf_ass.c b/libmpcodecs/vf_ass.c
index f33aba4..6640aef 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"
 
@@ -97,6 +99,7 @@ static int config(struct vf_instance_s* vf,
 		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);
 	}
+	dvbsub_configure(vf->priv->outw, vf->priv->outh);
 
 	return vf_next_config(vf, vf->priv->outw, vf->priv->outh, d_width, d_height, flags, outfmt);
 }
@@ -327,9 +330,12 @@ 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_frames)
+			images = dvbsub_render_frame(pts);
+		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..1169636 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);
@@ -397,6 +399,10 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i) {
                 sh_sub->extradata = malloc(codec->extradata_size);
                 memcpy(sh_sub->extradata, codec->extradata, codec->extradata_size);
                 sh_sub->extradata_len = codec->extradata_size;
+            } else if(codec->codec_id == CODEC_ID_DVB_SUBTITLE) {
+                sh_sub->extradata_len = sizeof(int);
+                sh_sub->extradata = malloc(sh_sub->extradata_len);
+                *(int *)sh_sub->extradata = codec->sub_id;
             }
             if (st->language)
               sh_sub->lang = strdup(st->language);
diff --git a/mp_msg.h b/mp_msg.h
index 0f2776f..2ba6d7c 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..673d3f1 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
@@ -66,7 +67,6 @@ if (HAVE_CMOV)
 #endif /* ARCH_X86 */
 }
 
-
 void update_subtitles(sh_video_t *sh_video, demux_stream_t *d_dvdsub, int reset)
 {
     unsigned char *packet=NULL;
@@ -144,7 +144,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;
@@ -168,6 +168,8 @@ void update_subtitles(sh_video_t *sh_video, demux_stream_t *d_dvdsub, int reset)
                     ass_process_chunk(ass_track, packet, len,
                                       (long long)(pts*1000 + 0.5),
                                       (long long)((endpts-pts)*1000 + 0.5));
+                } else if (type == 'b') { // DVB subs
+                    dvbsub_process_packet(packet, len, pts);
                 } else { // plaintext subs with libass
                     vo_sub = NULL;
                     if (pts != MP_NOPTS_VALUE) {
-------------- 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/eaf2e63c/attachment.pgp>


More information about the MPlayer-dev-eng mailing list