/* 2004.02.01 first released source code for IOMP */ /* * Copyright (C) 2000-2002 the xine project * * This file is part of xine, a free video player. * * xine 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. * * xine 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * $Id: xine_decoder.c,v 1.2 2003/11/25 04:26:02 georgedon Exp $ * */ #include #include #include #include #include #include #include #include #include "buffer.h" #include "xine_internal.h" #include "xineutils.h" #include "osd.h" /* #define LOG 1 */ #define SUB_MAX_TEXT 5 #define SUB_BUFSIZE 1024 typedef enum { SUBTITLE_SIZE_TINY = 0, SUBTITLE_SIZE_SMALL, SUBTITLE_SIZE_NORMAL, SUBTITLE_SIZE_LARGE, SUBTITLE_SIZE_NUM /* number of values in enum */ } subtitle_size; #define FONTNAME_SIZE 100 typedef struct sputext_class_s { spu_decoder_class_t class; subtitle_size subtitle_size; /* size of subtitles */ int vertical_offset; char font[FONTNAME_SIZE]; /* subtitle font */ char *src_encoding; /* encoding of subtitle file */ xine_t *xine; } sputext_class_t; typedef struct sputext_decoder_s { spu_decoder_t spu_decoder; sputext_class_t *class; xine_stream_t *stream; int lines; char text[SUB_MAX_TEXT][SUB_BUFSIZE]; /* below 3 variables are the same from class. use to detect * when something changes. */ subtitle_size subtitle_size; /* size of subtitles */ int vertical_offset; char font[FONTNAME_SIZE]; /* subtitle font */ int width; /* frame width */ int height; /* frame height */ int font_size; int line_height; int seek_count; int master_started; int slave_started; osd_renderer_t *renderer; osd_object_t *osd; int64_t img_duration; int64_t last_subtitle_end; /* no new subtitle before this vpts */ } sputext_decoder_t; static void update_font_size (sputext_decoder_t *this) { static int sizes[SUBTITLE_SIZE_NUM] = { 16, 20, 24, 32 }; int y; this->font_size = sizes[this->class->subtitle_size]; this->line_height = this->font_size + 10; y = this->height - (SUB_MAX_TEXT * this->line_height) - 5; if(((y - this->class->vertical_offset) >= 0) && ((y - this->class->vertical_offset) <= this->height)) y -= this->class->vertical_offset; if( this->osd ) this->renderer->free_object (this->osd); if(this->renderer) { this->osd = this->renderer->new_object (this->renderer, this->width, SUB_MAX_TEXT * this->line_height); this->renderer->set_font (this->osd, this->class->font, this->font_size); this->renderer->set_position (this->osd, 0, y); } } static void draw_subtitle(sputext_decoder_t *this, int64_t sub_start, int64_t sub_end ) { int line, y; int font_size; /* update settings from class */ if( this->subtitle_size != this->class->subtitle_size || this->vertical_offset != this->class->vertical_offset ) { this->subtitle_size = this->class->subtitle_size; this->vertical_offset = this->class->vertical_offset; update_font_size(this); } if( strcmp(this->font, this->class->font) ) { strcpy(this->font, this->class->font); if( this->renderer ) this->renderer->set_font (this->osd, this->class->font, this->font_size); } this->renderer->filled_rect (this->osd, 0, 0, this->width-1, this->line_height * SUB_MAX_TEXT - 1, 0); y = (SUB_MAX_TEXT - this->lines) * this->line_height; font_size = this->font_size; this->renderer->set_encoding(this->osd, this->class->src_encoding); for (line=0; linelines; line++) { int w,h,x; while(1) { this->renderer->get_text_size( this->osd, this->text[line], &w, &h); x = (this->width - w) / 2; if( w > this->width && font_size > 16 ) { font_size -= 4; this->renderer->set_font (this->osd, this->class->font, font_size); } else { break; } } this->renderer->render_text (this->osd, x, y + line*this->line_height, this->text[line], OSD_TEXT1); } if( font_size != this->font_size ) this->renderer->set_font (this->osd, this->class->font, this->font_size); if( this->last_subtitle_end && sub_start < this->last_subtitle_end ) { sub_start = this->last_subtitle_end; } this->last_subtitle_end = sub_end; this->renderer->set_text_palette (this->osd, -1, OSD_TEXT1); this->renderer->show (this->osd, sub_start); this->renderer->hide (this->osd, sub_end); #ifdef LOG printf ("sputext: scheduling subtitle >%s< at %lld until %lld, current time is %lld\n", this->text[0], sub_start, sub_end, this->stream->xine->clock->get_current_time (this->stream->xine->clock)); #endif } static void spudec_decode_data (spu_decoder_t *this_gen, buf_element_t *buf) { sputext_decoder_t *this = (sputext_decoder_t *) this_gen; int uses_time; int32_t start, end, diff; int64_t start_vpts, end_vpts; int64_t spu_offset; int i; uint32_t *val; char *str; extra_info_t extra_info; int status; /* filter unwanted streams */ if (buf->decoder_flags & BUF_FLAG_PREVIEW) return; if ((this->stream->spu_channel & 0x1f) != (buf->type & 0x1f)) return; val = (uint32_t * )buf->content; this->lines = *val++; uses_time = *val++; start = *val++; end = *val++; str = (char *)val; for (i = 0; i < this->lines; i++, str+=strlen(str)+1) { strcpy( this->text[i], str ); } #ifdef LOG printf("libsputext: decoder data [%s]\n", this->text[0]); printf("libsputext: mode %d timing %d->%d\n", uses_time, start, end); #endif if( end <= start ) { #ifdef LOG printf("libsputext: discarding subtitle with invalid timing\n"); #endif } spu_offset = this->stream->master->metronom->get_option (this->stream->master->metronom, METRONOM_SPU_OFFSET); start += (spu_offset / 90); end += (spu_offset / 90); xine_get_current_info (this->stream->master, &extra_info, sizeof(extra_info) ); if( !this->seek_count ) { this->seek_count = extra_info.seek_count; } while(this->seek_count == extra_info.seek_count) { /* initialize decoder if needed */ if( !this->width || !this->height || !this->img_duration || !this->osd ) { if( this->stream->video_out->status(this->stream->video_out, NULL, &this->width, &this->height, &this->img_duration )) { if( this->width && this->height && this->img_duration ) { this->renderer = this->stream->osd_renderer; update_font_size (this); } } } if( this->osd ) { /* try to use frame number mode */ if( !uses_time && extra_info.frame_number ) { diff = end - extra_info.frame_number; /* discard old subtitles */ if( diff < 0 ) { #ifdef LOG printf("libsputext: discarding old\n"); #endif return; } diff = start - extra_info.frame_number; /* draw it if less than 1/2 second left */ if( diff < 90000/2 / this->img_duration ) { start_vpts = extra_info.vpts + diff * this->img_duration; end_vpts = start_vpts + (end-start) * this->img_duration; draw_subtitle(this, start_vpts, end_vpts); return; } } else { if( !uses_time ) { start = start * this->img_duration / 90; end = end * this->img_duration / 90; uses_time = 1; } diff = end - extra_info.input_time; /* discard old subtitles */ if( diff < 0 ) { #ifdef LOG printf("libsputext: discarding old\n"); #endif return; } diff = start - extra_info.input_time; /* draw it if less than 1/2 second left */ if( diff < 500 ) { start_vpts = extra_info.vpts + diff * 90; end_vpts = start_vpts + (end-start) * 90; draw_subtitle(this, start_vpts, end_vpts); return; } } } status = xine_get_status (this->stream->master); if( this->master_started && (status == XINE_STATUS_QUIT || status == XINE_STATUS_STOP) ) { #ifdef LOG printf("libsputext: master stopped\n"); #endif this->width = this->height = 0; return; } if( status == XINE_STATUS_PLAY ) this->master_started = 1; status = xine_get_status (this->stream); if( this->slave_started && (status == XINE_STATUS_QUIT || status == XINE_STATUS_STOP) ) { #ifdef LOG printf("libsputext: slave stopped\n"); #endif this->width = this->height = 0; return; } if( status == XINE_STATUS_PLAY ) this->slave_started = 1; xine_usec_sleep (50000); xine_get_current_info (this->stream->master, &extra_info, sizeof(extra_info) ); } #ifdef LOG printf("libsputext: seek_count mismatch\n"); #endif } static void spudec_reset (spu_decoder_t *this_gen) { sputext_decoder_t *this = (sputext_decoder_t *) this_gen; this->width = this->height = 0; this->seek_count = 0; } static void spudec_discontinuity (spu_decoder_t *this_gen) { /* sputext_decoder_t *this = (sputext_decoder_t *) this_gen; */ } static void spudec_dispose (spu_decoder_t *this_gen) { sputext_decoder_t *this = (sputext_decoder_t *) this_gen; if (this->osd) { this->renderer->free_object (this->osd); this->osd = NULL; } free(this); } static void update_vertical_offset(void *class_gen, xine_cfg_entry_t *entry) { sputext_class_t *class = (sputext_class_t *)class_gen; class->vertical_offset = entry->num_value; } static void update_osd_font(void *class_gen, xine_cfg_entry_t *entry) { sputext_class_t *class = (sputext_class_t *)class_gen; strcpy(class->font, entry->str_value); printf("libsputext: spu_font = %s\n", class->font ); } static void update_subtitle_size(void *class_gen, xine_cfg_entry_t *entry) { sputext_class_t *class = (sputext_class_t *)class_gen; class->subtitle_size = entry->num_value; } static spu_decoder_t *sputext_class_open_plugin (spu_decoder_class_t *class_gen, xine_stream_t *stream) { sputext_class_t *class = (sputext_class_t *)class_gen; sputext_decoder_t *this ; this = (sputext_decoder_t *) xine_xmalloc (sizeof (sputext_decoder_t)); this->spu_decoder.decode_data = spudec_decode_data; this->spu_decoder.reset = spudec_reset; this->spu_decoder.discontinuity = spudec_discontinuity; this->spu_decoder.dispose = spudec_dispose; this->spu_decoder.get_interact_info = NULL; this->spu_decoder.set_button = NULL; this->spu_decoder.dispose = spudec_dispose; this->class = class; this->stream = stream; return (spu_decoder_t *) this; } static void sputext_class_dispose (spu_decoder_class_t *this) { free (this); } static char *sputext_class_get_identifier (spu_decoder_class_t *this) { return "sputext"; } static char *sputext_class_get_description (spu_decoder_class_t *this) { return "external subtitle decoder plugin"; } static void update_src_encoding(void *class_gen, xine_cfg_entry_t *entry) { sputext_class_t *class = (sputext_class_t *)class_gen; class->src_encoding = entry->str_value; printf("libsputext: spu_src_encoding = %s\n", class->src_encoding ); } static void *init_spu_decoder_plugin (xine_t *xine, void *data) { static char *subtitle_size_strings[] = { "tiny", "small", "normal", "large", NULL }; sputext_class_t *this ; #ifdef LOG printf("libsputext: init class\n"); #endif this = (sputext_class_t *) xine_xmalloc (sizeof (sputext_class_t)); this->class.open_plugin = sputext_class_open_plugin; this->class.get_identifier = sputext_class_get_identifier; this->class.get_description = sputext_class_get_description; this->class.dispose = sputext_class_dispose; this->xine = xine; this->subtitle_size = xine->config->register_enum(xine->config, "misc.spu_subtitle_size", 1, subtitle_size_strings, _("Subtitle size (relative window size)"), NULL, 0, update_subtitle_size, this); this->vertical_offset = xine->config->register_num(xine->config, "misc.spu_vertical_offset", 0, _("Subtitle vertical offset (relative window size)"), NULL, 0, update_vertical_offset, this); strcpy(this->font, xine->config->register_string(xine->config, "misc.spu_font", "sans", _("Font for external subtitles"), NULL, 0, update_osd_font, this)); this->src_encoding = xine->config->register_string(xine->config, "misc.spu_src_encoding", "iso-8859-1", _("Encoding of subtitles"), NULL, 10, update_src_encoding, this); return &this->class; } /* plugin catalog information */ static uint32_t supported_types[] = { BUF_SPU_TEXT, 0 }; static decoder_info_t spudec_info = { supported_types, /* supported types */ 1 /* priority */ }; plugin_info_t xine_plugin_info[] = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_SPU_DECODER | PLUGIN_MUST_PRELOAD, 14, "sputext", XINE_VERSION_CODE, &spudec_info, &init_spu_decoder_plugin }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } };