[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