summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorreimar <reimar@b3059339-0415-0410-9bf9-f77b7e298cf2>2010-07-10 13:45:09 +0000
committerUoti Urpala <uau@glyph.nonexistent.invalid>2010-11-02 04:14:43 +0200
commit96c17fe68b12104269f70b113d757fbc67b5ed1f (patch)
treeb63db7e8a5b4d090210e621ab5ad948eaf12e788
parentd6322407c503bbca1f1bb48e06e275f7b610a3a8 (diff)
downloadmpv-96c17fe68b12104269f70b113d757fbc67b5ed1f.tar.bz2
mpv-96c17fe68b12104269f70b113d757fbc67b5ed1f.tar.xz
subs: Add support for PGS subtitle decoding via libavcodec
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@31665 b3059339-0415-0410-9bf9-f77b7e298cf2
-rw-r--r--Makefile1
-rw-r--r--av_sub.c103
-rw-r--r--av_sub.h29
-rw-r--r--libmpdemux/demux_lavf.c2
-rw-r--r--mpcommon.c46
-rw-r--r--spudec.c72
-rw-r--r--spudec.h4
7 files changed, 247 insertions, 10 deletions
diff --git a/Makefile b/Makefile
index 79500272e2..060bb5d3ab 100644
--- a/Makefile
+++ b/Makefile
@@ -111,6 +111,7 @@ SRCS_COMMON-$(LIBASS) += ass_mp.c \
SRCS_COMMON-$(LIBAVCODEC) += av_opts.c \
libaf/af_lavcac3enc.c \
+ av_sub.c \
libaf/af_lavcresample.c \
libmpcodecs/ad_ffmpeg.c \
libmpcodecs/vd_ffmpeg.c \
diff --git a/av_sub.c b/av_sub.c
new file mode 100644
index 0000000000..eba46fa6d8
--- /dev/null
+++ b/av_sub.c
@@ -0,0 +1,103 @@
+/*
+ * 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 <libavcodec/avcodec.h>
+
+#include "libmpdemux/stheader.h"
+#include "libvo/sub.h"
+#include "spudec.h"
+#include "av_sub.h"
+
+void reset_avsub(struct sh_sub *sh)
+{
+ if (sh->context) {
+ avcodec_close(sh->context);
+ av_freep(&sh->context);
+ }
+}
+
+/**
+ * Decode a subtitle packet via libavcodec.
+ * \return < 0 on error, > 0 if further processing is needed
+ */
+int decode_avsub(struct sh_sub *sh, uint8_t **data, int *size, double *pts, double *endpts)
+{
+ AVCodecContext *ctx = sh->context;
+ int new_type = 0;
+ int res;
+ int got_sub;
+ AVSubtitle sub;
+ AVPacket pkt;
+ av_init_packet(&pkt);
+ pkt.data = *data;
+ pkt.size = *size;
+ pkt.pts = *pts * 1000;
+ if (*pts != MP_NOPTS_VALUE && *endpts != MP_NOPTS_VALUE)
+ pkt.convergence_duration = (*endpts - *pts) * 1000;
+ if (!ctx) {
+ AVCodec *sub_codec;
+ avcodec_init();
+ avcodec_register_all();
+ ctx = avcodec_alloc_context();
+ sub_codec = avcodec_find_decoder(CODEC_ID_HDMV_PGS_SUBTITLE);
+ if (!ctx || !sub_codec || avcodec_open(ctx, sub_codec) < 0) {
+ mp_msg(MSGT_SUBREADER, MSGL_FATAL, "Could not open subtitle decoder\n");
+ av_freep(&ctx);
+ return -1;
+ }
+ sh->context = ctx;
+ }
+ res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, &pkt);
+ if (res < 0)
+ return res;
+ if (*pts != MP_NOPTS_VALUE) {
+ if (sub.end_display_time > sub.start_display_time)
+ *endpts = *pts + sub.end_display_time / 1000.0;
+ *pts += sub.start_display_time / 1000.0;
+ }
+ if (got_sub && sub.num_rects > 0) {
+ switch (sub.rects[0]->type) {
+ case SUBTITLE_BITMAP:
+ if (!vo_spudec)
+ vo_spudec = spudec_new(NULL);
+ spudec_set_paletted(vo_spudec,
+ sub.rects[0]->pict.data[0],
+ sub.rects[0]->pict.linesize[0],
+ sub.rects[0]->pict.data[1],
+ sub.rects[0]->x,
+ sub.rects[0]->y,
+ sub.rects[0]->w,
+ sub.rects[0]->h,
+ *pts,
+ *endpts);
+ vo_osd_changed(OSDTYPE_SPU);
+ break;
+ case SUBTITLE_TEXT:
+ *data = strdup(sub.rects[0]->text);
+ new_type = 't';
+ break;
+ case SUBTITLE_ASS:
+ *data = strdup(sub.rects[0]->ass);
+ new_type = 'a';
+ break;
+ }
+ }
+ if (got_sub)
+ ; // TODO: free sub once there is a free function...
+ return new_type;
+}
diff --git a/av_sub.h b/av_sub.h
new file mode 100644
index 0000000000..8713bd207e
--- /dev/null
+++ b/av_sub.h
@@ -0,0 +1,29 @@
+/*
+ * 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_AV_SUB_H
+#define MPLAYER_AV_SUB_H
+
+#include <stdint.h>
+
+struct sh_sub;
+
+void reset_avsub(struct sh_sub *sh);
+int decode_avsub(struct sh_sub *sh, uint8_t **data, int *size, double *pts, double *endpts);
+
+#endif
diff --git a/libmpdemux/demux_lavf.c b/libmpdemux/demux_lavf.c
index 9344356418..f94770fe3c 100644
--- a/libmpdemux/demux_lavf.c
+++ b/libmpdemux/demux_lavf.c
@@ -427,6 +427,8 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i) {
type = 'v';
else if(codec->codec_id == CODEC_ID_DVB_TELETEXT)
type = 'd';
+ else if(codec->codec_id == CODEC_ID_HDMV_PGS_SUBTITLE)
+ type = 'p';
else
break;
sh_sub = new_sh_sub_sid(demuxer, i, priv->sub_streams);
diff --git a/mpcommon.c b/mpcommon.c
index 47706ed497..66f2eece8c 100644
--- a/mpcommon.c
+++ b/mpcommon.c
@@ -17,6 +17,8 @@
*/
#include <stdlib.h>
+#include <stdbool.h>
+
#include "mpcommon.h"
#include "options.h"
#include "stream/stream.h"
@@ -30,6 +32,7 @@
#include "spudec.h"
#include "version.h"
#include "vobsub.h"
+#include "av_sub.h"
#include "libmpcodecs/dec_teletext.h"
#include "ffmpeg_files/intreadwrite.h"
#include "m_option.h"
@@ -84,6 +87,15 @@ if (HAVE_CMOV)
#endif /* ARCH_X86 */
}
+static bool is_text_sub(int type)
+{
+ return type == 't' || type == 'm' || type == 'a';
+}
+
+static bool is_av_sub(int type)
+{
+ return type == 'p';
+}
void update_subtitles(struct MPContext *mpctx, struct MPOpts *opts,
sh_video_t *sh_video, double refpts, double sub_offset,
@@ -92,7 +104,7 @@ void update_subtitles(struct MPContext *mpctx, struct MPOpts *opts,
double curpts = refpts + sub_delay;
unsigned char *packet=NULL;
int len;
- char type = d_dvdsub->sh ? ((sh_sub_t *)d_dvdsub->sh)->type : 'v';
+ int type = d_dvdsub->sh ? ((sh_sub_t *)d_dvdsub->sh)->type : 'v';
static subtitle subs;
if (reset) {
sub_clear_text(&subs, MP_NOPTS_VALUE);
@@ -103,6 +115,10 @@ void update_subtitles(struct MPContext *mpctx, struct MPOpts *opts,
spudec_reset(vo_spudec);
vo_osd_changed(OSDTYPE_SPU);
}
+#ifdef CONFIG_LIBAVCODEC
+ if (is_av_sub(type))
+ reset_avsub(d_dvdsub->sh);
+#endif
return;
}
// find sub
@@ -122,7 +138,6 @@ void update_subtitles(struct MPContext *mpctx, struct MPOpts *opts,
if (vo_spudec && (vobsub_id >= 0 || (opts->sub_id >= 0 && type == 'v'))) {
int timestamp;
current_module = "spudec";
- spudec_heartbeat(vo_spudec, 90000*curpts);
/* Get a sub packet from the DVD or a vobsub */
while(1) {
// Vobsub
@@ -157,12 +172,7 @@ void update_subtitles(struct MPContext *mpctx, struct MPOpts *opts,
if (vo_vobsub || timestamp >= 0)
spudec_assemble(vo_spudec, packet, len, timestamp);
}
-
- if (spudec_changed(vo_spudec))
- vo_osd_changed(OSDTYPE_SPU);
- } else if (opts->sub_id >= 0
- && (type == 't' || type == 'm' || type == 'a' || type == 'd')) {
- double endpts;
+ } else if (opts->sub_id >= 0 && (is_text_sub(type) || is_av_sub(type) || type == 'd')) {
if (type == 'd' && !d_dvdsub->demuxer->teletext) {
tt_stream_props tsp = {0};
void *ptr = &tsp;
@@ -171,18 +181,28 @@ void update_subtitles(struct MPContext *mpctx, struct MPOpts *opts,
}
if (d_dvdsub->non_interleaved)
ds_get_next_pts(d_dvdsub);
+
+ int orig_type = type;
while (d_dvdsub->first) {
double subpts = ds_get_next_pts(d_dvdsub) + sub_offset;
+ type = orig_type;
if (subpts > curpts) {
// Libass handled subs can be fed to it in advance
- if (!opts->ass_enabled || type == 'd')
+ if (!opts->ass_enabled || !is_text_sub(type))
break;
// Try to avoid demuxing whole file at once
if (d_dvdsub->non_interleaved && subpts > curpts + 1)
break;
}
- endpts = d_dvdsub->first->endpts + sub_offset;
+ double endpts = d_dvdsub->first->endpts + sub_offset;
len = ds_get_packet_sub(d_dvdsub, &packet);
+ if (is_av_sub(type)) {
+#ifdef CONFIG_LIBAVCODEC
+ type = decode_avsub(d_dvdsub->sh, &packet, &len, &subpts, &endpts);
+ if (type <= 0)
+#endif
+ continue;
+ }
if (type == 'm') {
if (len < 2) continue;
len = FFMIN(len - 2, AV_RB16(packet));
@@ -251,6 +271,12 @@ void update_subtitles(struct MPContext *mpctx, struct MPOpts *opts,
if (sub_clear_text(&subs, curpts))
set_osd_subtitle(mpctx, &subs);
}
+ if (vo_spudec) {
+ spudec_heartbeat(vo_spudec, 90000*curpts);
+ if (spudec_changed(vo_spudec))
+ vo_osd_changed(OSDTYPE_SPU);
+ }
+
current_module=NULL;
}
diff --git a/spudec.c b/spudec.c
index 84af67e563..6c5213bc92 100644
--- a/spudec.c
+++ b/spudec.c
@@ -59,7 +59,9 @@ extern int sub_pos;
typedef struct packet_t packet_t;
struct packet_t {
+ int is_decoded;
unsigned char *packet;
+ int data_len;
unsigned int palette[4];
unsigned int alpha[4];
unsigned int control_start; /* index of start of control data */
@@ -629,9 +631,28 @@ void spudec_heartbeat(void *this, unsigned int pts100)
packet_t *packet = spudec_dequeue_packet(spu);
spu->start_pts = packet->start_pts;
spu->end_pts = packet->end_pts;
+ if (packet->is_decoded) {
+ free(spu->image);
+ spu->image_size = packet->data_len;
+ spu->image = packet->packet;
+ spu->aimage = packet->packet + packet->stride * packet->height;
+ packet->packet = NULL;
+ spu->width = packet->width;
+ spu->height = packet->height;
+ spu->stride = packet->stride;
+ spu->start_col = packet->start_col;
+ spu->start_row = packet->start_row;
+
+ // TODO use correct values
+ spu->scaled_frame_width = 0;
+ spu->scaled_frame_height = 0;
+ spu->orig_frame_width = 1920;
+ spu->orig_frame_height = 1080;
+ } else {
if (spu->auto_palette)
compute_palette(spu, packet);
spudec_process_data(spu, packet);
+ }
spudec_free_packet(packet);
spu->spu_changed = 1;
}
@@ -1260,3 +1281,54 @@ void spudec_set_hw_spu(void *this, struct vo *hw_spu)
spu->hw_spu = hw_spu;
vo_control(hw_spu, VOCTRL_SET_SPU_PALETTE, spu->global_palette);
}
+
+#define MP_NOPTS_VALUE (-1LL<<63) //both int64_t and double should be able to represent this exactly
+
+/**
+ * palette must contain at least 256 32-bit entries, otherwise crashes
+ * are possible
+ */
+void spudec_set_paletted(void *this, const uint8_t *pal_img, int pal_stride,
+ const void *palette,
+ int x, int y, int w, int h,
+ double pts, double endpts)
+{
+ packet_t *packet;
+ const uint32_t *pal = palette;
+ spudec_handle_t *spu = this;
+ uint8_t *img;
+ uint8_t *aimg;
+ int stride = (w + 7) & ~7;
+ if ((unsigned)w >= 0x8000 || (unsigned)h > 0x4000)
+ return;
+ packet = calloc(1, sizeof(packet_t));
+ packet->is_decoded = 1;
+ packet->width = w;
+ packet->height = h;
+ packet->stride = stride;
+ packet->start_col = x;
+ packet->start_row = y;
+ packet->data_len = 2 * stride * h;
+ packet->packet = malloc(packet->data_len);
+ img = packet->packet;
+ aimg = packet->packet + stride * h;
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++) {
+ uint32_t pixel = pal[pal_img[x]];
+ *aimg++ = -(pixel >> 24);
+ *img++ = (((pixel & 0x000000ff) >> 0) +
+ ((pixel & 0x0000ff00) >> 7) +
+ ((pixel & 0x00ff0000) >> 16)) >> 2;
+ }
+ for (; x < stride; x++)
+ *aimg++ = *img++ = 0;
+ pal_img += pal_stride;
+ }
+ packet->start_pts = 0;
+ packet->end_pts = 0x7fffffff;
+ if (pts != MP_NOPTS_VALUE)
+ packet->start_pts = pts * 90000;
+ if (endpts != MP_NOPTS_VALUE)
+ packet->end_pts = endpts * 90000;
+ spudec_queue_packet(spu, packet);
+}
diff --git a/spudec.h b/spudec.h
index c1fd449413..45568279c3 100644
--- a/spudec.h
+++ b/spudec.h
@@ -36,5 +36,9 @@ void spudec_set_hw_spu(void *this, struct vo *hw_spu);
int spudec_changed(void *this);
void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox);
void spudec_set_forced_subs_only(void * const this, const unsigned int flag);
+void spudec_set_paletted(void *this, const uint8_t *pal_img, int stride,
+ const void *palette,
+ int x, int y, int w, int h,
+ double pts, double endpts);
#endif /* MPLAYER_SPUDEC_H */