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 + -
显示快捷键?