changeset 31599:cafeb7863de8

Add support for PGS subtitle decoding via libavcodec.
author reimar
date Sat, 10 Jul 2010 13:45:09 +0000
parents 6c3667fb9423
children 22157f6d154e
files Makefile av_sub.c av_sub.h libmpdemux/demux_lavf.c mpcommon.c spudec.c spudec.h
diffstat 7 files changed, 242 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sat Jul 10 13:43:04 2010 +0000
+++ b/Makefile	Sat Jul 10 13:45:09 2010 +0000
@@ -122,6 +122,7 @@
                                         libass/ass_utils.c \
 
 SRCS_COMMON-$(LIBAVCODEC)            += av_opts.c \
+                                        av_sub.c \
                                         libaf/af_lavcresample.c \
                                         libmpcodecs/ad_ffmpeg.c \
                                         libmpcodecs/vd_ffmpeg.c \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/av_sub.c	Sat Jul 10 13:45:09 2010 +0000
@@ -0,0 +1,102 @@
+/*
+ * 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/av_sub.h	Sat Jul 10 13:45:09 2010 +0000
@@ -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
--- a/libmpdemux/demux_lavf.c	Sat Jul 10 13:43:04 2010 +0000
+++ b/libmpdemux/demux_lavf.c	Sat Jul 10 13:45:09 2010 +0000
@@ -431,6 +431,8 @@
                 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);
--- a/mpcommon.c	Sat Jul 10 13:43:04 2010 +0000
+++ b/mpcommon.c	Sat Jul 10 13:45:09 2010 +0000
@@ -29,6 +29,7 @@
 #include "spudec.h"
 #include "version.h"
 #include "vobsub.h"
+#include "av_sub.h"
 #include "libmpcodecs/dec_teletext.h"
 #include "libavutil/intreadwrite.h"
 #include "m_option.h"
@@ -84,14 +85,22 @@
 #endif /* ARCH_X86 */
 }
 
+static int is_text_sub(int type)
+{
+    return type == 't' || type == 'm' || type == 'a';
+}
+
+static int is_av_sub(int type)
+{
+    return type == 'p';
+}
 
 void update_subtitles(sh_video_t *sh_video, double refpts, demux_stream_t *d_dvdsub, int reset)
 {
     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 text_sub = type == 't' || type == 'm' || type == 'a' || type == 'd';
+    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);
@@ -102,6 +111,10 @@
             spudec_reset(vo_spudec);
             vo_osd_changed(OSDTYPE_SPU);
         }
+#ifdef CONFIG_LIBAVCODEC
+        if (is_av_sub(type))
+            reset_avsub(d_dvdsub->sh);
+#endif
     }
     // find sub
     if (subdata) {
@@ -121,7 +134,6 @@
         (vobsub_id >= 0 || (dvdsub_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
@@ -156,10 +168,8 @@
             if (vo_vobsub || timestamp >= 0)
                 spudec_assemble(vo_spudec, packet, len, timestamp);
         }
-
-        if (spudec_changed(vo_spudec))
-            vo_osd_changed(OSDTYPE_SPU);
-    } else if (dvdsub_id >= 0 && text_sub) {
+    } else if (dvdsub_id >= 0 && (is_text_sub(type) || is_av_sub(type) || type == 'd')) {
+        int orig_type = type;
         double endpts;
         if (type == 'd' && !d_dvdsub->demuxer->teletext) {
             tt_stream_props tsp = {0};
@@ -171,9 +181,17 @@
             ds_get_next_pts(d_dvdsub);
         while (1) {
             double subpts = curpts;
+            type = orig_type;
             len = ds_get_packet_sub(d_dvdsub, &packet, &subpts, &endpts);
             if (len < 0)
                 break;
+            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));
@@ -244,9 +262,15 @@
             if (d_dvdsub->non_interleaved)
                 ds_get_next_pts(d_dvdsub);
         }
-        if (text_sub && sub_clear_text(&subs, curpts))
+        if (sub_clear_text(&subs, curpts))
             set_osd_subtitle(&subs);
     }
+    if (vo_spudec) {
+        spudec_heartbeat(vo_spudec, 90000*curpts);
+        if (spudec_changed(vo_spudec))
+            vo_osd_changed(OSDTYPE_SPU);
+    }
+
     current_module=NULL;
 }
 
--- a/spudec.c	Sat Jul 10 13:43:04 2010 +0000
+++ b/spudec.c	Sat Jul 10 13:45:09 2010 +0000
@@ -59,7 +59,9 @@
 
 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 @@
     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 @@
   spu->hw_spu = hw_spu;
   hw_spu->control(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);
+}
--- a/spudec.h	Sat Jul 10 13:43:04 2010 +0000
+++ b/spudec.h	Sat Jul 10 13:45:09 2010 +0000
@@ -36,5 +36,9 @@
 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 */