static int jacosub_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *avpkt) { AVSubtitle *sub = data; const char *ptr = avpkt->data; if (avpkt->size <= 0) goto end; if (*ptr) { AVBPrint buffer; char *dec_sub; // skip timers ptr = jss_skip_whitespace(ptr); ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++; ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++; av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE); jacosub_to_ass(avctx, &buffer, ptr); av_bprint_finalize(&buffer, &dec_sub); ff_ass_add_rect(sub, dec_sub, avpkt->pts, avpkt->duration, 0); av_free(dec_sub); } end: *got_sub_ptr = sub->num_rects > 0; return avpkt->size; }
static int jacosub_read_header(AVFormatContext *s) { AVBPrint header; AVIOContext *pb = s->pb; char line[JSS_MAX_LINESIZE]; JACOsubContext *jacosub = s->priv_data; int shift_set = 0; // only the first shift matters int merge_line = 0; int i, ret; AVStream *st = avformat_new_stream(s, NULL); if (!st) return AVERROR(ENOMEM); avpriv_set_pts_info(st, 64, 1, 100); st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; st->codec->codec_id = AV_CODEC_ID_JACOSUB; jacosub->timeres = 30; av_bprint_init(&header, 1024+FF_INPUT_BUFFER_PADDING_SIZE, 4096); while (!avio_feof(pb)) { int cmd_len; const char *p = line; int64_t pos = avio_tell(pb); int len = ff_get_line(pb, line, sizeof(line)); p = jss_skip_whitespace(p); /* queue timed line */ if (merge_line || timed_line(p)) { AVPacket *sub; sub = ff_subtitles_queue_insert(&jacosub->q, line, len, merge_line); if (!sub) return AVERROR(ENOMEM); sub->pos = pos; merge_line = len > 1 && !strcmp(&line[len - 2], "\\\n"); continue; } /* skip all non-compiler commands and focus on the command */ if (*p != '#') continue; p++; i = get_jss_cmd(p[0]); if (i == -1) continue; /* trim command + spaces */ cmd_len = strlen(cmds[i]); if (av_strncasecmp(p, cmds[i], cmd_len) == 0) p += cmd_len; else p++; p = jss_skip_whitespace(p); /* handle commands which affect the whole script */ switch (cmds[i][0]) { case 'S': // SHIFT command affect the whole script... if (!shift_set) { jacosub->shift = get_shift(jacosub->timeres, p); shift_set = 1; } av_bprintf(&header, "#S %s", p); break; case 'T': // ...but must be placed after TIMERES jacosub->timeres = strtol(p, NULL, 10); if (!jacosub->timeres) jacosub->timeres = 30; else av_bprintf(&header, "#T %s", p); break; } } /* general/essential directives in the extradata */ ret = avpriv_bprint_to_extradata(st->codec, &header); if (ret < 0) return ret; /* SHIFT and TIMERES affect the whole script so packet timing can only be * done in a second pass */ for (i = 0; i < jacosub->q.nb_subs; i++) { AVPacket *sub = &jacosub->q.subs[i]; read_ts(jacosub, sub->data, &sub->pts, &sub->duration); } ff_subtitles_queue_finalize(&jacosub->q); return 0; }
static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src) { int i, valign = 0, halign = 0; char c = av_toupper(*src); char directives[128] = {0}; /* extract the optional directives */ if ((c >= 'A' && c <= 'Z') || c == '[') { char *p = directives; char *pend = directives + sizeof(directives) - 1; do *p++ = av_toupper(*src++); while (*src && !jss_whitespace(*src) && p < pend); *p = 0; src = jss_skip_whitespace(src); } /* handle directives (TODO: handle more of them, and more reliably) */ if (strstr(directives, "VB")) valign = ALIGN_VB; else if (strstr(directives, "VM")) valign = ALIGN_VM; else if (strstr(directives, "VT")) valign = ALIGN_VT; if (strstr(directives, "JC")) halign = ALIGN_JC; else if (strstr(directives, "JL")) halign = ALIGN_JL; else if (strstr(directives, "JR")) halign = ALIGN_JR; if (valign || halign) { if (!valign) valign = ALIGN_VB; if (!halign) halign = ALIGN_JC; switch (valign | halign) { case ALIGN_VB | ALIGN_JL: av_bprintf(dst, "{\\an1}"); break; // bottom left case ALIGN_VB | ALIGN_JC: av_bprintf(dst, "{\\an2}"); break; // bottom center case ALIGN_VB | ALIGN_JR: av_bprintf(dst, "{\\an3}"); break; // bottom right case ALIGN_VM | ALIGN_JL: av_bprintf(dst, "{\\an4}"); break; // middle left case ALIGN_VM | ALIGN_JC: av_bprintf(dst, "{\\an5}"); break; // middle center case ALIGN_VM | ALIGN_JR: av_bprintf(dst, "{\\an6}"); break; // middle right case ALIGN_VT | ALIGN_JL: av_bprintf(dst, "{\\an7}"); break; // top left case ALIGN_VT | ALIGN_JC: av_bprintf(dst, "{\\an8}"); break; // top center case ALIGN_VT | ALIGN_JR: av_bprintf(dst, "{\\an9}"); break; // top right } } /* process timed line */ while (*src && *src != '\n') { /* text continue on the next line */ if (src[0] == '\\' && src[1] == '\n') { src += 2; while (jss_whitespace(*src)) src++; continue; } /* special character codes */ for (i = 0; i < FF_ARRAY_ELEMS(ass_codes_map); i++) { const char *from = ass_codes_map[i].from; const char *arg = ass_codes_map[i].arg; size_t codemap_len = strlen(from); if (!strncmp(src, from, codemap_len)) { src += codemap_len; src += ass_codes_map[i].func(dst, src, arg); break; } } /* simple char copy */ if (i == FF_ARRAY_ELEMS(ass_codes_map)) av_bprintf(dst, "%c", *src++); } av_bprintf(dst, "\r\n"); }
static int jacosub_read_header(AVFormatContext *s) { AVBPrint header; AVIOContext *pb = s->pb; char line[JSS_MAX_LINESIZE]; JACOsubContext *jacosub = s->priv_data; int shift_set = 0; // only the first shift matters int merge_line = 0; int i; AVStream *st = avformat_new_stream(s, NULL); if (!st) return AVERROR(ENOMEM); avpriv_set_pts_info(st, 64, 1, 100); st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; st->codec->codec_id = CODEC_ID_JACOSUB; jacosub->timeres = 30; av_bprint_init(&header, 1024+FF_INPUT_BUFFER_PADDING_SIZE, 4096); while (!url_feof(pb)) { int cmd_len; const char *p = line; int64_t pos = avio_tell(pb); ff_get_line(pb, line, sizeof(line)); p = jss_skip_whitespace(p); /* queue timed line */ if (merge_line || timed_line(p)) { SubEntry *subs, *sub; const int len = strlen(line); if (merge_line) { char *tmp; const int old_len = strlen(sub->line); sub = &subs[jacosub->nsub]; tmp = av_realloc(sub->line, old_len + len + 1); if (!tmp) return AVERROR(ENOMEM); sub->line = tmp; strcpy(sub->line + old_len, line); } else { subs = av_realloc(jacosub->subs, sizeof(*jacosub->subs) * (jacosub->nsub+1)); if (!subs) return AVERROR(ENOMEM); jacosub->subs = subs; sub = &subs[jacosub->nsub]; sub->pos = pos; sub->line = av_strdup(line); if (!sub->line) return AVERROR(ENOMEM); } merge_line = len > 1 && !strcmp(&line[len - 2], "\\\n"); if (!merge_line) jacosub->nsub++; continue; } /* skip all non-compiler commands and focus on the command */ if (*p != '#') continue; p++; i = get_jss_cmd(p[0]); if (i == -1) continue; /* trim command + spaces */ cmd_len = strlen(cmds[i]); if (av_strncasecmp(p, cmds[i], cmd_len) == 0) p += cmd_len; else p++; p = jss_skip_whitespace(p); /* handle commands which affect the whole script */ switch (cmds[i][0]) { case 'S': // SHIFT command affect the whole script... if (!shift_set) { jacosub->shift = get_shift(jacosub->timeres, p); shift_set = 1; } av_bprintf(&header, "#S %s", p); break; case 'T': // ...but must be placed after TIMERES jacosub->timeres = strtol(p, NULL, 10); av_bprintf(&header, "#T %s", p); break; } } /* general/essential directives in the extradata */ av_bprint_finalize(&header, (char **)&st->codec->extradata); st->codec->extradata_size = header.len + 1; /* SHIFT and TIMERES affect the whole script so packet timing can only be * done in a second pass */ for (i = 0; i < jacosub->nsub; i++) { SubEntry *sub = &jacosub->subs[i]; read_ts(jacosub, sub->line, &sub->start, &sub->end); } qsort(jacosub->subs, jacosub->nsub, sizeof(*jacosub->subs), cmp_timed_sub); return 0; }