/***************************************************************************** * asa: portable digital subtitle renderer ***************************************************************************** * Copyright (C) 2007 David Lamparter * * 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 ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "acconf.h" #endif #include #include #include #include #include #include #include #include #include #include #include "render.h" csri_rend *r; static int do_usage(FILE *fd) { fprintf(fd, "usage: csri [COMMON-OPTIONS] COMMAND [COMMAND-OPTIONS]\n" "\n" "common options: [-r renderer [-s specific]]\n" "\t-r\tselect a renderer, by name\n" "\t-s\tselect a renderer version, by specific name\n" "\n" "commands:\n" "\tlist\tshow installed renderers\n" "\tinfo\tshow detailed renderer information\n" "\trender\trender subtitle output\n" #ifdef HAVE_LIBPNG "\t\t-i FILE\t\tread background from PNG file\n" "\t\t-o PREFIX\twrite output to PREFIX_nnnn.png\n" #endif "\t\t-A\t\tkeep alpha\n" "\t\t-t [start][:[end][:[step]]]\tspecify timestamps to be rendered\n" "\t\tSUBFILE\t\tsubtitle file to load\n" "\n"); return 2; } static int do_list(int argc, char **argv) { unsigned num = 0; if (argc) return do_usage(stderr); while (r) { struct csri_info *info = csri_renderer_info(r); if (!info) continue; printf("%s:%s %s, %s, %s\n", info->name, info->specific, info->longname, info->author, info->copyright); r = csri_renderer_next(r); num++; } fprintf(stderr, "%u renderers found\n", num); return num > 0 ? 0 : 1; } static csri_ext_id known_exts[] = { CSRI_EXT_OPENERR, CSRI_EXT_LOGGING, CSRI_EXT_STREAM, CSRI_EXT_STREAM_ASS, CSRI_EXT_STREAM_TEXT, CSRI_EXT_STREAM_DISCARD, NULL }; static const char *dummy_script = "[Script Info]\r\n" "ScriptType: v4.00\r\n" "[V4 Styles]\r\n" "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, " "TertiaryColour, BackColour, Bold, Italic, BorderStyle, " "Outline, Shadow, Alignment, MarginL, MarginR, MarginV, " "AlphaLevel, Encoding\r\n" "Style: Default,Arial,20,&HFFFFFF&,&H00FFFF&,&H000000&,&H000000&," "0,0,1,2,2,2,10,10,10,0,0\r\n" "[Events]\r\n" "Format: Marked, Start, End, Style, Name, " "MarginL, MarginR, MarginV, Effect, Text\r\n" "Dialogue: Marked=0,0:00:01.00,0:00:02.00,Default,,0000,0000,0000,," "test\r\n"; static const char *dummy_stream = "1,0,Default,,0000,0000,0000,,stream\r\n"; #define e(x) { #x, CSRI_F_ ## x } #define ree(x) e(RGB ## x), e(x ## RGB), e(BGR ## x), e(x ## BGR) static struct csri_fmtlistent { const char *label; enum csri_pixfmt fmt; } csri_fmts[] = { ree(A), ree(_), e(RGB), e(BGR), e(AYUV), e(YUVA), e(YVUA), e(YUY2), e(YV12A), e(YV12), { NULL, 0 } }; static void listfmts() { csri_inst *i; struct csri_fmtlistent *fmt; struct csri_fmt f; printf("\ntrying to get list of supported colorspaces:\n"); fflush(stdout); i = csri_open_mem(r, dummy_script, strlen(dummy_script), NULL); f.width = f.height = 256; for (fmt = csri_fmts; fmt->label; fmt++) { f.pixfmt = fmt->fmt; if (!csri_request_fmt(i, &f)) printf("\t[%04x] %s\n", fmt->fmt, fmt->label); } csri_close(i); } static int do_info(int argc, char **argv) { struct csri_info *info; csri_ext_id *id; if (argc) return do_usage(stderr); info = csri_renderer_info(r); if (!info) return 1; printf("%s:%s\n\t%s\n\t%s\n\t%s\n", info->name, info->specific, info->longname, info->author, info->copyright); printf("supported CSRI extensions:\n"); for (id = known_exts; *id; id++) { void *rext = csri_query_ext(r, *id); void *lext = csri_query_ext(NULL, *id); if (lext || rext) { printf("\t%s ", *id); if (!lext) printf("\n"); else if (!rext) printf("[library only]\n"); else if (rext == lext) printf("[emulated by library]\n"); else printf("\n"); } } listfmts(); return 0; } static void logfunc(void *appdata, enum csri_logging_severity sev, const char *message) { char severity[32]; switch (sev) { case CSRI_LOG_DEBUG: strcpy(severity, "[debug]"); break; case CSRI_LOG_INFO: strcpy(severity, "[info]"); break; case CSRI_LOG_NOTICE: strcpy(severity, "[notice]"); break; case CSRI_LOG_WARNING: strcpy(severity, "[warning]"); break; case CSRI_LOG_ERROR: strcpy(severity, "[error]"); break; default: snprintf(severity, 32, "[%d?]", (int)sev); } fprintf(stderr, "%-10s %s\n", severity, message); } static int real_render(double *times, const char *infile, const char *outfile, const char *script, enum csri_pixfmt pfmt) { struct csri_frame *bg, *a; csri_inst *inst; struct csri_fmt fmt; double now; int idx; uint32_t width = 640, height = 480; bg = infile ? png_load(infile, &width, &height, pfmt) : frame_alloc(width, height, pfmt); a = frame_alloc(width, height, pfmt); if (!bg || !a) { fprintf(stderr, "failed to allocate frame\n"); if (!bg) fprintf(stderr, "\t- problem with background.\n"); return 2; } inst = csri_open_file(r, script, NULL); if (!inst) { fprintf(stderr, "failed to open script \"%s\"\n", script); return 2; } fmt.pixfmt = pfmt; fmt.width = width; fmt.height = height; if (csri_request_fmt(inst, &fmt)) { fprintf(stderr, "format not supported by renderer\n"); return 2; } idx = 0; for (now = times[0]; now <= times[1]; now += times[2]) { frame_copy(a, bg, width, height); csri_render(inst, a, now); if (outfile) { char buffer[256]; snprintf(buffer, sizeof(buffer), "%s_%04d.png", outfile, idx); printf("%s\n", buffer); png_store(a, buffer, width, height); } idx++; } csri_close(inst); inst = NULL; frame_free(bg); frame_free(a); return 0; } static int do_render(int argc, char **argv) { double times[3] = {0.0, 0.0, 1.0}; const char *outfile = NULL, *infile = NULL; struct csri_fmtlistent *fmte; int keepalpha = 0; enum csri_pixfmt fmt = ~0U; argv--, argc++; while (1) { int c, i; const char *short_options = "t:o:i:F:A"; char *arg, *end, *err; struct option long_options[] = { {"time", 1, 0, 't'}, {"output", 1, 0, 'o'}, {"input", 1, 0, 'i'}, {"format", 0, 0, 'F'}, {"alpha", 0, 0, 'A'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) break; switch (c) { case 't': arg = optarg; for (i = 0; i < 3; i++) { end = strchr(arg, ':'); if (end) *end = '\0'; if (*arg) { times[i] = strtod(arg, &err); if (*err) { fprintf(stderr, "invalid time: %s\n", arg); return do_usage(stderr); } } if (!end) break; arg = end + 1; } break; case 'i': infile = optarg; break; case 'o': outfile = optarg; break; case 'A': if (fmt != ~0U) return do_usage(stderr); keepalpha = 1; break; case 'F': if (keepalpha || fmt != ~0U) return do_usage(stderr); for (fmte = csri_fmts; fmte->label; fmte++) if (!strcmp(fmte->label, optarg)) break; if (!fmte->label) return do_usage(stderr); fmt = fmte->fmt; break; default: return do_usage(stderr); }; } if (fmt == ~0U) fmt = keepalpha ? CSRI_F_RGBA : CSRI_F_RGB_; if (!isfinite(times[0])) { fprintf(stderr, "invalid start time\n"); return do_usage(stderr); } if (!isfinite(times[1]) || times[1] < times[0]) { fprintf(stderr, "invalid end time\n"); return do_usage(stderr); } if (!isnormal(times[2]) || times[2] < 0.0) { fprintf(stderr, "invalid end time\n"); return do_usage(stderr); } if (argc - optind != 1) { fprintf(stderr, "script name missing\n"); return do_usage(stderr); } return real_render(times, infile, outfile, argv[optind], fmt); } static int do_streamtest(int argc, char **argv) { const char *outfile = NULL; struct csri_fmtlistent *fmte; enum csri_pixfmt pfmt = ~0U; struct csri_frame *bg, *a; csri_inst *inst; struct csri_fmt fmt; uint32_t width = 640, height = 480; struct csri_stream_ext *sext; argv--, argc++; while (1) { int c; const char *short_options = "o:F:"; struct option long_options[] = { {"output", 1, 0, 'o'}, {"format", 0, 0, 'F'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) break; switch (c) { case 'o': outfile = optarg; break; case 'F': if (pfmt != ~0U) return do_usage(stderr); for (fmte = csri_fmts; fmte->label; fmte++) if (!strcmp(fmte->label, optarg)) break; if (!fmte->label) return do_usage(stderr); pfmt = fmte->fmt; break; default: return do_usage(stderr); }; } if (pfmt == ~0U) pfmt = CSRI_F_RGB_; sext = (struct csri_stream_ext *)csri_query_ext(r, CSRI_EXT_STREAM_ASS); if (!sext) { fprintf(stderr, "renderer does not support ASS streaming\n"); return 2; } bg = frame_alloc(width, height, pfmt); a = frame_alloc(width, height, pfmt); if (!bg || !a) { fprintf(stderr, "failed to allocate frame\n"); return 2; } inst = sext->init_stream(r, dummy_script, strlen(dummy_script), NULL); if (!inst) { fprintf(stderr, "failed to initialize stream\n"); return 2; } fmt.pixfmt = pfmt; fmt.width = width; fmt.height = height; if (csri_request_fmt(inst, &fmt)) { fprintf(stderr, "format not supported by renderer\n"); return 2; } frame_copy(a, bg, width, height); csri_render(inst, a, 1.75); if (outfile) { char buffer[256]; snprintf(buffer, sizeof(buffer), "%s_nstream.png", outfile); printf("%s\n", buffer); png_store(a, buffer, width, height); } frame_copy(a, bg, width, height); sext->push_packet(inst, dummy_stream, strlen(dummy_stream), 1.5, 2.0); csri_render(inst, a, 1.75); if (outfile) { char buffer[256]; snprintf(buffer, sizeof(buffer), "%s_stream.png", outfile); printf("%s\n", buffer); png_store(a, buffer, width, height); } csri_close(inst); inst = NULL; frame_free(bg); frame_free(a); return 0; } int main(int argc, char **argv) { struct csri_logging_ext *logext; if (argc < 2) return do_usage(stderr); logext = (struct csri_logging_ext *)csri_query_ext(NULL, CSRI_EXT_LOGGING); if (logext && logext->set_logcallback) logext->set_logcallback(logfunc, NULL); else fprintf(stderr, "warning: unable to set log callback\n"); r = csri_renderer_default(); argc--, argv++; if (!strcmp(argv[0], "list")) return do_list(argc - 1, argv + 1); if (!strcmp(argv[0], "-r")) { const char *name = NULL, *spec = NULL; if (argc < 2) return do_usage(stderr); name = argv[1]; argc -= 2, argv += 2; if (!strcmp(argv[0], "-s")) { if (argc < 2) return do_usage(stderr); spec = argv[1]; } r = csri_renderer_byname(name, spec); if (!r) { fprintf(stderr, "renderer %s:%s not found.\n", name, spec ? spec : "*"); return 2; } } if (!strcmp(argv[0], "info")) return do_info(argc - 1, argv + 1); if (!strcmp(argv[0], "render")) return do_render(argc - 1, argv + 1); if (!strcmp(argv[0], "streamtest")) return do_streamtest(argc - 1, argv + 1); return do_usage(stderr); }