# HG changeset patch # User reimar # Date 1278769509 0 # Node ID cafeb7863de87a1f49850b8c4c5f5888d9b7f2fe # Parent 6c3667fb942309da198583c996302e17bbf8fcfb Add support for PGS subtitle decoding via libavcodec. diff -r 6c3667fb9423 -r cafeb7863de8 Makefile --- 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 \ diff -r 6c3667fb9423 -r cafeb7863de8 av_sub.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; +} diff -r 6c3667fb9423 -r cafeb7863de8 av_sub.h --- /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 + +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 -r 6c3667fb9423 -r cafeb7863de8 libmpdemux/demux_lavf.c --- 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); diff -r 6c3667fb9423 -r cafeb7863de8 mpcommon.c --- 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; } diff -r 6c3667fb9423 -r cafeb7863de8 spudec.c --- 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); +} diff -r 6c3667fb9423 -r cafeb7863de8 spudec.h --- 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 */