ass.c

来自「君正早期ucos系统(只有早期的才不没有打包成库),MPLAYER,文件系统,图」· C语言 代码 · 共 1,093 行 · 第 1/2 页

C
1,093
字号
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-// vim:ts=8:sw=8:noet:ai:/*  Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>  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 St, Fifth Floor, Boston, MA 02110-1301 USA*/#include "config.h"#include <mplaylib.h>#include "mplaylib.h"#include <assert.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <inttypes.h>#ifdef USE_ICONV#include <iconv.h>#endif#include "ass.h"#include "ass_utils.h"#include "ass_library.h"#include "mputils.h"typedef enum {PST_UNKNOWN = 0, PST_INFO, PST_STYLES, PST_EVENTS, PST_FONTS} parser_state_t;struct parser_priv_s {	parser_state_t state;	char* fontname;	char* fontdata;	int fontdata_size;	int fontdata_used;};#define ASS_STYLES_ALLOC 20#define ASS_EVENTS_ALLOC 200void ass_free_track(ass_track_t* track) {	int i;		if (track->parser_priv) {		if (track->parser_priv->fontname)			free(track->parser_priv->fontname);		if (track->parser_priv->fontdata)			free(track->parser_priv->fontdata);		free(track->parser_priv);	}	if (track->style_format)		free(track->style_format);	if (track->event_format)		free(track->event_format);	if (track->styles) {		for (i = 0; i < track->n_styles; ++i)			ass_free_style(track, i);		free(track->styles);	}	if (track->events) {		for (i = 0; i < track->n_events; ++i)			ass_free_event(track, i);		free(track->events);	}}/// \brief Allocate a new style struct/// \param track track/// \return style idint ass_alloc_style(ass_track_t* track) {	int sid;		assert(track->n_styles <= track->max_styles);	if (track->n_styles == track->max_styles) {		track->max_styles += ASS_STYLES_ALLOC;		track->styles = (ass_style_t*)realloc(track->styles, sizeof(ass_style_t)*track->max_styles);	}		sid = track->n_styles++;	memset(track->styles + sid, 0, sizeof(ass_style_t));	return sid;}/// \brief Allocate a new event struct/// \param track track/// \return event idint ass_alloc_event(ass_track_t* track) {	int eid;		assert(track->n_events <= track->max_events);	if (track->n_events == track->max_events) {		track->max_events += ASS_EVENTS_ALLOC;		track->events = (ass_event_t*)realloc(track->events, sizeof(ass_event_t)*track->max_events);	}		eid = track->n_events++;	memset(track->events + eid, 0, sizeof(ass_event_t));	return eid;}void ass_free_event(ass_track_t* track, int eid) {	ass_event_t* event = track->events + eid;	if (event->Name)		free(event->Name);	if (event->Effect)		free(event->Effect);	if (event->Text)		free(event->Text);	if (event->render_priv)		free(event->render_priv);}void ass_free_style(ass_track_t* track, int sid) {	ass_style_t* style = track->styles + sid;	if (style->Name)		free(style->Name);	if (style->FontName)		free(style->FontName);}// ==============================================================================================static void skip_spaces(char** str) {	char* p = *str;	while ((*p==' ') || (*p=='\t'))		++p;	*str = p;}static void rskip_spaces(char** str, char* limit) {	char* p = *str;	while ((p >= limit) && ((*p==' ') || (*p=='\t')))		--p;	*str = p;}/** * \brief find style by name * \param track track * \param name style name * \return index in track->styles * Returnes 0 if no styles found => expects at least 1 style. * Parsing code always adds "Default" style in the end. */static int lookup_style(ass_track_t* track, char* name) {	int i;	if (*name == '*') ++name; // FIXME: what does '*' really mean ?	for (i=0; i<track->n_styles; ++i) {		// FIXME: mb strcasecmp ?		if (strcmp(track->styles[i].Name, name) == 0)			return i;	}	i = track->default_style;	mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoStyleNamedXFoundUsingY, track, name, track->styles[i].Name);	return i; // use the first style}static uint32_t string2color(char* p) {	uint32_t tmp;	(void)strtocolor(&p, &tmp);	return tmp;}static long long string2timecode(char* p) {	unsigned h, m, s, ms;	long long tm;	int res = sscanf(p, "%1d:%2d:%2d.%2d", &h, &m, &s, &ms);	if (res < 4) {		mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_BadTimestamp);		return 0;	}	tm = ((h * 60 + m) * 60 + s) * 1000 + ms * 10;	return tm;}/** * \brief converts numpad-style align to align. */static int numpad2align(int val) {	int res, v;	v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment	if (v != 0) v = 3 - v;	res = ((val - 1) % 3) + 1; // horizontal alignment	res += v*4;	return res;}#define NEXT(str,token) \	token = next_token(&str); \	if (!token) break;#define ANYVAL(name,func) \	} else if (strcasecmp(tname, #name) == 0) { \		target->name = func(token); \		mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token);#define STRVAL(name) \	} else if (strcasecmp(tname, #name) == 0) { \		if (target->name != NULL) free(target->name); \		target->name = strdup(token); \		mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token);		#define COLORVAL(name) ANYVAL(name,string2color)#define INTVAL(name) ANYVAL(name,atoi)#define FPVAL(name) ANYVAL(name,atof)#define TIMEVAL(name) ANYVAL(name,string2timecode)#define STYLEVAL(name) \	} else if (strcasecmp(tname, #name) == 0) { \		target->name = lookup_style(track, token); \		mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token);#define ALIAS(alias,name) \	if (strcasecmp(tname, #alias) == 0) {tname = #name;}static char* next_token(char** str) {	char* p = *str;	char* start;	skip_spaces(&p);	if (*p == '\0') {		*str = p;		return 0;	}	start = p; // start of the token	for (; (*p != '\0') && (*p != ','); ++p) {}	if (*p == '\0') {		*str = p; // eos found, str will point to '\0' at exit	} else {		*p = '\0';		*str = p + 1; // ',' found, str will point to the next char (beginning of the next token)	}	--p; // end of current token	rskip_spaces(&p, start);	if (p < start)		p = start; // empty token	else		++p; // the first space character, or '\0'	*p = '\0';	return start;}/** * \brief Parse the tail of Dialogue line * \param track track * \param event parsed data goes here * \param str string to parse, zero-terminated * \param n_ignored number of format options to skip at the beginning*/ static int process_event_tail(ass_track_t* track, ass_event_t* event, char* str, int n_ignored){	char* token;	char* tname;	char* p = str;	int i;	ass_event_t* target = event;	char* format = strdup(track->event_format);	char* q = format; // format scanning pointer	if (track->n_styles == 0) {		// add "Default" style to the end		// will be used if track does not contain a default style (or even does not contain styles at all)		int sid = ass_alloc_style(track);		track->styles[sid].Name = strdup("Default");		track->styles[sid].FontName = strdup("Arial");	}	for (i = 0; i < n_ignored; ++i) {		NEXT(q, tname);	}	while (1) {		NEXT(q, tname);		if (strcasecmp(tname, "Text") == 0) {			char* last;			event->Text = strdup(p);			if (*event->Text != 0) {				last = event->Text + strlen(event->Text) - 1;				if (last >= event->Text && *last == '\r')					*last = 0;			}			mp_msg(MSGT_ASS, MSGL_DBG2, "Text = %s\n", event->Text);			event->Duration -= event->Start;			free(format);			return 0; // "Text" is always the last		}		NEXT(p, token);		ALIAS(End,Duration) // temporarily store end timecode in event->Duration		if (0) { // cool ;)			INTVAL(Layer)			STYLEVAL(Style)			STRVAL(Name)			STRVAL(Effect)			INTVAL(MarginL)			INTVAL(MarginR)			INTVAL(MarginV)			TIMEVAL(Start)			TIMEVAL(Duration)		}	}	free(format);	return 1;}/** * \brief Parse command line style overrides (--ass-force-style option) * \param track track to apply overrides to * The format for overrides is [StyleName.]Field=Value */void process_force_style(ass_track_t* track) {	char **fs, *eq, *dt, *style, *tname, *token;	ass_style_t* target;	int sid;	char** list = track->library->style_overrides;		if (!list) return;		for (fs = list; *fs; ++fs) {		eq = strrchr(*fs, '=');		if (!eq)			continue;		*eq = '\0';		token = eq + 1;		dt = strrchr(*fs, '.');		if (dt) {			*dt = '\0';			style = *fs;			tname = dt + 1;		} else {			style = NULL;			tname = *fs;		}		for (sid = 0; sid < track->n_styles; ++sid) {			if (style == NULL || strcasecmp(track->styles[sid].Name, style) == 0) {				target = track->styles + sid;				if (0) {					STRVAL(FontName)					COLORVAL(PrimaryColour)					COLORVAL(SecondaryColour)					COLORVAL(OutlineColour)					COLORVAL(BackColour)					FPVAL(FontSize)					INTVAL(Bold)					INTVAL(Italic)					INTVAL(Underline)					INTVAL(StrikeOut)					FPVAL(Spacing)					INTVAL(Angle)					INTVAL(BorderStyle)					INTVAL(Alignment)					INTVAL(MarginL)					INTVAL(MarginR)					INTVAL(MarginV)					INTVAL(Encoding)					FPVAL(ScaleX)					FPVAL(ScaleY)					FPVAL(Outline)					FPVAL(Shadow)				}			}		}		*eq = '=';		if (dt) *dt = '.';	}}/** * \brief Parse the Style line * \param track track * \param str string to parse, zero-terminated * Allocates a new style struct.*/ static int process_style(ass_track_t* track, char *str){	char* token;	char* tname;	char* p = str;	char* format;	char* q; // format scanning pointer	int sid;	ass_style_t* style;	ass_style_t* target;	if (!track->style_format) {		// no style format header		// probably an ancient script version		if (track->track_type == TRACK_TYPE_SSA)			track->style_format = strdup("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour,"					"TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline,"					"Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding");		else			track->style_format = strdup("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour,"					"OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut,"					"ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow,"					"Alignment, MarginL, MarginR, MarginV, Encoding");	}	q = format = strdup(track->style_format);		mp_msg(MSGT_ASS, MSGL_V, "[%p] Style: %s\n", track, str);		sid = ass_alloc_style(track);	style = track->styles + sid;	target = style;// fill style with some default values	style->ScaleX = 100.;	style->ScaleY = 100.;		while (1) {		NEXT(q, tname);		NEXT(p, token);		//		ALIAS(TertiaryColour,OutlineColour) // ignore TertiaryColour; it appears only in SSA, and is overridden by BackColour					if (0) { // cool ;)			STRVAL(Name)				if ((strcmp(target->Name, "Default")==0) || (strcmp(target->Name, "*Default")==0))					track->default_style = sid;			STRVAL(FontName)			COLORVAL(PrimaryColour)			COLORVAL(SecondaryColour)			COLORVAL(OutlineColour) // TertiaryColor			COLORVAL(BackColour)				// SSA uses BackColour for both outline and shadow				// this will destroy SSA's TertiaryColour, but i'm not going to use it anyway				if (track->track_type == TRACK_TYPE_SSA)					target->OutlineColour = target->BackColour;			FPVAL(FontSize)			INTVAL(Bold)			INTVAL(Italic)			INTVAL(Underline)			INTVAL(StrikeOut)			FPVAL(Spacing)			INTVAL(Angle)			INTVAL(BorderStyle)			INTVAL(Alignment)				if (track->track_type == TRACK_TYPE_ASS)					target->Alignment = numpad2align(target->Alignment);			INTVAL(MarginL)			INTVAL(MarginR)			INTVAL(MarginV)			INTVAL(Encoding)			FPVAL(ScaleX)			FPVAL(ScaleY)			FPVAL(Outline)			FPVAL(Shadow)		}	}	style->ScaleX /= 100.;	style->ScaleY /= 100.;	style->Bold = !!style->Bold;	style->Italic = !!style->Italic;	style->Underline = !!style->Underline;	if (!style->Name)		style->Name = strdup("Default");	if (!style->FontName)		style->FontName = strdup("Arial");	free(format);	return 0;	}static int process_styles_line(ass_track_t* track, char *str){	if (!strncmp(str,"Format:", 7)) {		char* p = str + 7;		skip_spaces(&p);		track->style_format = strdup(p);		mp_msg(MSGT_ASS, MSGL_DBG2, "Style format: %s\n", track->style_format);	} else if (!strncmp(str,"Style:", 6)) {		char* p = str + 6;		skip_spaces(&p);		process_style(track, p);	}	return 0;}static int process_info_line(ass_track_t* track, char *str){	if (!strncmp(str, "PlayResX:", 9)) {		track->PlayResX = atoi(str + 9);	} else if (!strncmp(str,"PlayResY:", 9)) {		track->PlayResY = atoi(str + 9);	} else if (!strncmp(str,"Timer:", 6)) {		track->Timer = atof(str + 6);	} else if (!strncmp(str,"WrapStyle:", 10)) {		track->WrapStyle = atoi(str + 10);	}	return 0;}static int process_events_line(ass_track_t* track, char *str){	if (!strncmp(str, "Format:", 7)) {		char* p = str + 7;		skip_spaces(&p);		track->event_format = strdup(p);		mp_msg(MSGT_ASS, MSGL_DBG2, "Event format: %s\n", track->event_format);	} else if (!strncmp(str, "Dialogue:", 9)) {		// This should never be reached for embedded subtitles.		// They have slightly different format and are parsed in ass_process_chunk,		// called directly from demuxer		int eid;		ass_event_t* event;				str += 9;		skip_spaces(&str);		eid = ass_alloc_event(track);		event = track->events + eid;		process_event_tail(track, event, str, 0);	} else {		mp_msg(MSGT_ASS, MSGL_V, "Not understood: %s  \n", str);	}	return 0;}// Copied from mkvtoolnixstatic unsigned char* decode_chars(unsigned char c1, unsigned char c2,		unsigned char c3, unsigned char c4, unsigned char* dst, int cnt){	uint32_t value;	unsigned char bytes[3];	int i;	value = ((c1 - 33) << 18) + ((c2 - 33) << 12) + ((c3 - 33) << 6) + (c4 - 33);	bytes[2] = value & 0xff;	bytes[1] = (value & 0xff00) >> 8;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?