Commit bb1dbbdf authored by Laurent Aimar's avatar Laurent Aimar

Added preliminary subtitle decoder support to our avcodec wrapper.

Only blu-ray subtitle are enabled.
If other formats are to be enabled, they must be carefully tested.
parent 2a89d905
......@@ -2,6 +2,7 @@ SOURCES_avcodec = \
avcodec.c \
avcodec.h \
video.c \
subtitle.c \
audio.c \
deinterlace.c \
avutil.h \
......
......@@ -280,6 +280,11 @@ static int OpenDecoder( vlc_object_t *p_this )
i_result = InitAudioDec ( p_dec, p_context, p_codec,
i_codec_id, psz_namecodec );
break;
case SPU_ES:
p_dec->pf_decode_sub = DecodeSubtitle;
i_result = InitSubtitleDec( p_dec, p_context, p_codec,
i_codec_id, psz_namecodec );
break;
default:
i_result = VLC_EGENERIC;
}
......@@ -305,6 +310,9 @@ static void CloseDecoder( vlc_object_t *p_this )
case VIDEO_ES:
EndVideoDec ( p_dec );
break;
case SPU_ES:
EndSubtitleDec( p_dec );
break;
}
if( p_sys->p_context )
......
......@@ -33,6 +33,7 @@ int GetVlcChroma( video_format_t *fmt, const int i_ffmpeg_chroma );
picture_t * DecodeVideo ( decoder_t *, block_t ** );
aout_buffer_t * DecodeAudio( decoder_t *, block_t ** );
subpicture_t *DecodeSubtitle( decoder_t *p_dec, block_t ** );
/* Video encoder module */
int OpenEncoder ( vlc_object_t * );
......@@ -58,6 +59,12 @@ int InitAudioDec( decoder_t *p_dec, AVCodecContext *p_context,
AVCodec *p_codec, int i_codec_id, const char *psz_namecodec );
void EndAudioDec( decoder_t *p_dec );
/* Subtitle Decoder */
int InitSubtitleDec( decoder_t *p_dec, AVCodecContext *p_context,
AVCodec *p_codec, int i_codec_id, const char *psz_namecodec );
void EndSubtitleDec( decoder_t *p_dec );
/*****************************************************************************
* Module descriptor help strings
*****************************************************************************/
......
......@@ -371,13 +371,17 @@ static const struct
#endif
/* Subtitle streams */
/* Before this version, subs were too experimental */
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 52, 33, 0 )
{ VLC_CODEC_BD_PG, CODEC_ID_HDMV_PGS_SUBTITLE, SPU_ES },
#endif
#if 0
{ VLC_CODEC_SPU, CODEC_ID_DVD_SUBTITLE, SPU_ES },
{ VLC_CODEC_DVBS, CODEC_ID_DVB_SUBTITLE, SPU_ES },
{ VLC_CODEC_SUBT, CODEC_ID_TEXT, SPU_ES },
{ VLC_CODEC_XSUB, CODEC_ID_XSUB, SPU_ES },
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 51, 50, 0 )
{ VLC_CODEC_SSA, CODEC_ID_SSA, SPU_ES },
#endif
#endif
{ 0, 0, UNKNOWN_ES }
......
/*****************************************************************************
* subtitle.c: subtitle decoder using ffmpeg library
*****************************************************************************
* Copyright (C) 2009 Laurent Aimar
* $Id$
*
* Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <assert.h>
#include <vlc_common.h>
#include <vlc_codec.h>
#include <vlc_avcodec.h>
#include <vlc_osd.h>
/* ffmpeg header */
#ifdef HAVE_LIBAVCODEC_AVCODEC_H
# include <libavcodec/avcodec.h>
# ifdef HAVE_AVCODEC_VAAPI
# include <libavcodec/vaapi.h>
# endif
#elif defined(HAVE_FFMPEG_AVCODEC_H)
# include <ffmpeg/avcodec.h>
#else
# include <avcodec.h>
#endif
#include "avcodec.h"
struct decoder_sys_t {
FFMPEG_COMMON_MEMBERS
};
static subpicture_t *ConvertSubtitle(decoder_t *, AVSubtitle *, mtime_t pts);
/**
* Initialize subtitle decoder
*/
int InitSubtitleDec(decoder_t *dec, AVCodecContext *context,
AVCodec *codec, int codec_id, const char *namecodec)
{
decoder_sys_t *sys;
/* */
dec->p_sys = sys = malloc(sizeof(*sys));
if (!sys)
return VLC_ENOMEM;
sys->p_context = context;
sys->p_codec = codec;
sys->i_codec_id = codec_id;
sys->psz_namecodec = namecodec;
sys->b_delayed_open = false;
/* */
context->extradata_size = 0;
context->extradata = NULL;
/* */
vlc_avcodec_lock();
if (avcodec_open(context, codec) < 0) {
vlc_avcodec_unlock();
msg_Err(dec, "cannot open codec (%s)", namecodec);
free(context->extradata);
free(sys);
return VLC_EGENERIC;
}
vlc_avcodec_unlock();
/* */
msg_Dbg(dec, "ffmpeg codec (%s) started", namecodec);
dec->fmt_out.i_cat = SPU_ES;
return VLC_SUCCESS;
}
/**
* Decode one subtitle
*/
subpicture_t *DecodeSubtitle(decoder_t *dec, block_t **block_ptr)
{
decoder_sys_t *sys = dec->p_sys;
if (!block_ptr || !*block_ptr)
return NULL;
block_t *block = *block_ptr;
if (block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) {
block_Release(block);
avcodec_flush_buffers(sys->p_context);
return NULL;
}
if (block->i_buffer <= 0) {
block_Release(block);
return NULL;
}
*block_ptr =
block = block_Realloc(block,
0,
block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE);
if (!block)
return NULL;
block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE;
memset(&block->p_buffer[block->i_buffer], 0, FF_INPUT_BUFFER_PADDING_SIZE);
/* */
AVSubtitle subtitle;
memset(&subtitle, 0, sizeof(subtitle));
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = block->p_buffer;
pkt.size = block->i_buffer;
int has_subtitle = 0;
int used = avcodec_decode_subtitle2(sys->p_context,
&subtitle, &has_subtitle, &pkt);
if (used < 0) {
msg_Warn(dec, "cannot decode one subtitle (%zu bytes)",
block->i_buffer);
block_Release(block);
return NULL;
} else if ((size_t)used > block->i_buffer) {
used = block->i_buffer;
}
block->i_buffer -= used;
block->p_buffer += used;
/* */
subpicture_t *spu = NULL;
if (has_subtitle)
spu = ConvertSubtitle(dec, &subtitle,
block->i_pts > 0 ? block->i_pts : block->i_dts);
/* */
if (!spu)
block_Release(block);
return spu;
}
/**
* Clean up private data
*/
void EndSubtitleDec(decoder_t *dec)
{
VLC_UNUSED(dec);
}
/**
* Convert a RGBA ffmpeg region to our format.
*/
static subpicture_region_t *ConvertRegionRGBA(AVSubtitleRect *ffregion)
{
video_format_t fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.i_chroma = VLC_FOURCC('R','G','B','A');
fmt.i_aspect = 0;
fmt.i_width =
fmt.i_visible_width = ffregion->w;
fmt.i_height =
fmt.i_visible_height = ffregion->h;
fmt.i_x_offset = 0;
fmt.i_y_offset = 0;
subpicture_region_t *region = subpicture_region_New(&fmt);
if (!region)
return NULL;
region->i_x = ffregion->x;
region->i_y = ffregion->y;
region->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
const plane_t *p = &region->p_picture->p[0];
for (int y = 0; y < ffregion->h; y++) {
for (int x = 0; x < ffregion->w; x++) {
/* I don't think don't have paletized RGB_A_ */
const uint8_t index = ffregion->pict.data[0][y * ffregion->w+x];
assert(index < ffregion->nb_colors);
uint32_t color;
memcpy(&color, &ffregion->pict.data[1][4*index], 4);
uint8_t *p_rgba = &p->p_pixels[y * p->i_pitch + x * p->i_pixel_pitch];
p_rgba[0] = (color >> 16) & 0xff;
p_rgba[1] = (color >> 8) & 0xff;
p_rgba[2] = (color >> 0) & 0xff;
p_rgba[3] = (color >> 24) & 0xff;
}
}
return region;
}
/**
* Convert a ffmpeg subtitle to our format.
*/
static subpicture_t *ConvertSubtitle(decoder_t *dec, AVSubtitle *ffsub, mtime_t pts)
{
subpicture_t *spu = decoder_NewSubpicture(dec);
if (!spu)
return NULL;
//msg_Err(dec, "%lld %d %d",
// pts, ffsub->start_display_time, ffsub->end_display_time);
spu->i_start = pts + ffsub->start_display_time * INT64_C(1000);
spu->i_stop = pts + ffsub->end_display_time * INT64_C(1000);
spu->b_absolute = true; /* FIXME How to set it right ? */
spu->b_ephemer = true; /* FIXME How to set it right ? */
spu->i_original_picture_width =
dec->fmt_in.subs.spu.i_original_frame_width;
spu->i_original_picture_height =
dec->fmt_in.subs.spu.i_original_frame_height;
subpicture_region_t **region_next = &spu->p_region;
for (unsigned i = 0; i < ffsub->num_rects; i++) {
AVSubtitleRect *rec = ffsub->rects[i];
msg_Err(dec, "SUBS RECT[%d]: %dx%d @%dx%d",
i, rec->w, rec->h, rec->x, rec->y);
subpicture_region_t *region;
switch (ffsub->format) {
case 0:
region = ConvertRegionRGBA(rec);
break;
default:
msg_Warn(dec, "unsupported subtitle type");
region = NULL;
break;
}
if (region) {
*region_next = region;
region_next = &region->p_next;
}
/* Free AVSubtitleRect
* FIXME isn't there an avcodec function ? */
free(rec->pict.data[0]); /* Plane */
free(rec->pict.data[1]); /* Palette */
free(rec);
}
free(ffsub->rects);
return spu;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment