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

Nicolas George nicolas.george at normalesup.org
Sat Feb 28 12:17:14 CET 2009


Le decadi 10 ventôse, an CCXVII, Uoti Urpala a écrit :
> I didn't read it in detail but it seems sane. The subtitle code overall
> should be factored more like audio and video decoders instead of
> everything directly calling functions specific to one subtitle type, but
> fixing all the existing subtitle code is a separate task...

This is exactly what I thought while writing my patch.

> The added line has a tab, should be spaces.

Fixed.

> This stuff should be in a context struct to allow multiple decoder
> instances. Even if the address of that struct is kept in a global/static
> for now (I don't remember whether there already is some suitable place
> to keep it - at least in svn there probably isn't).

I changed it that way. There is a single global variable, dvbsub_current,
with the same role as ass_track.


Le decadi 10 ventôse, an CCXVII, Reimar Döffinger a écrit :
> Surrouinding code uses tabs.

Fixed.

> what is "sus" supposed to stand for?

Typo, fixed.

> > +    if (extradata_size == sizeof(int))
> > +        decoder->sub_id = *(int *)extradata;
> I doubt this is correct, extradata should be architecture-independent,
> i.e. be a fixed size, not sizeof(int) and always litte- or big-endian,
> not native-endian.

This was a hack: there is a sub_id field in the AVFormatContext created by
lavf for each stream, it was not copied. As dvbsub do not have extradata, I
used it to store the sub_id.

I changed it with a new field in the structure.

> Can this validly happen? If not, either remove it or make it an assert.

It could in case of malloc failure; some of the rest of mplayer's code does
not really care about malloc failures, and it is probably right.

Anyway, I changed somewhat the structure and it is no longer relevant.

> > +	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);
> > +	}
> I guess there is no way to display both currently?

No. In fact, with the current, more correct version of the code,
dvbsub_frames and ass_track can not be both non-NULL at the same time.

Making it possible to display several subtitles tracks at once may be a task
for later.

> extra space

Fixed.

> cosmetic

Fixed.

Regards,

-- 
  Nicolas George
-------------- next part --------------
 Makefile                |    1 +
 command.c               |    4 +
 dvbsub.c                |  219 +++++++++++++++++++++++++++++++++++++++++++++++
 dvbsub.h                |   18 ++++
 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, 279 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..0b06540
--- /dev/null
+++ b/dvbsub.c
@@ -0,0 +1,219 @@
+#include "mplayer.h"
+#include "mp_msg.h"
+#include "libavcodec/avcodec.h"
+#include "dvbsub.h"
+
+typedef struct dvbsub_frame dvbsub_frame_t;
+
+struct dvbsub_track {
+    dvbsub_frame_t *frames;
+    AVCodecContext *decoder;
+    ass_image_t *images;
+    int frame_w;
+    int frame_h;
+    int rendered;
+};
+
+struct dvbsub_frame {
+    dvbsub_frame_t *next;
+    AVSubtitleRect **rect;
+    float start, end;
+    int n_rect;
+};
+
+dvbsub_track_t *dvbsub_current = NULL;
+
+static void dvbsub_invalidate_images(dvbsub_track_t *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(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_track_t *dvbsub_create(int sub_id)
+{
+    dvbsub_track_t *track;
+    int r;
+    AVCodec *codec;
+
+    track = malloc(sizeof(dvbsub_track_t));
+    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(dvbsub_track_t *track)
+{
+    dvbsub_frame_t *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(dvbsub_track_t *track, 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 = 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(dvbsub_track_t *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(dvbsub_track_t *track, double pts,
+    int frame_w, int frame_h)
+{
+    dvbsub_frame_t *frame, *nf;
+    ass_image_t *ni, **tail;
+    int i, psize, x, y, w, h, dx, dy, stride;
+    uint8_t (*palette)[4];
+    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 = (uint8_t (*)[4])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++)][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;
+    track->rendered = 1;
+    return track->images;
+}
diff --git a/dvbsub.h b/dvbsub.h
new file mode 100644
index 0000000..96f1b49
--- /dev/null
+++ b/dvbsub.h
@@ -0,0 +1,18 @@
+#ifndef MPLAYER_DVBSUB_H
+#define MPLAYER_DVBSUB_H
+
+#include <stdint.h>
+#include "libass/ass.h"
+
+typedef struct dvbsub_track dvbsub_track_t;
+
+extern dvbsub_track_t *dvbsub_current;
+
+dvbsub_track_t *dvbsub_create(int sub_id);
+void dvbsub_destroy(dvbsub_track_t *track);
+void dvbsub_process_packet(dvbsub_track_t *track, uint8_t *packet, int size,
+    double pts);
+ass_image_t *dvbsub_render_frame(dvbsub_track_t *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..8705183 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
+  dvbsub_track_t *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/fd643178/attachment.pgp>


More information about the MPlayer-dev-eng mailing list