static int read_dialogue(ASSContext *ass, AVBPrint *dst, const uint8_t *p, int64_t *start, int *duration) { int pos = 0; int64_t end; int hh1, mm1, ss1, ms1; int hh2, mm2, ss2, ms2; if (sscanf(p, "Dialogue: %*[^,],%d:%d:%d%*c%d,%d:%d:%d%*c%d,%n", &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2, &pos) >= 8 && pos > 0) { /* This is not part of the sscanf itself in order to handle an actual * number (which would be the Layer) or the form "Marked=N" (which is * the old SSA field, now replaced by Layer, and will lead to Layer * being 0 here). */ const int layer = atoi(p + 10); end = (hh2*3600LL + mm2*60LL + ss2) * 100LL + ms2; *start = (hh1*3600LL + mm1*60LL + ss1) * 100LL + ms1; *duration = end - *start; av_bprint_clear(dst); av_bprintf(dst, "%u,%d,%s", ass->readorder++, layer, p + pos); /* right strip the buffer */ while (dst->len > 0 && dst->str[dst->len - 1] == '\r' || dst->str[dst->len - 1] == '\n') dst->str[--dst->len] = 0; return 0; } return -1; }
static int sami_read_header(AVFormatContext *s) { SAMIContext *sami = s->priv_data; AVStream *st = avformat_new_stream(s, NULL); AVBPrint buf, hdr_buf; char c = 0; int res = 0, got_first_sync_point = 0; FFTextReader tr; ff_text_init_avio(&tr, s->pb); if (!st) return AVERROR(ENOMEM); avpriv_set_pts_info(st, 64, 1, 1000); st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; st->codec->codec_id = AV_CODEC_ID_SAMI; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); av_bprint_init(&hdr_buf, 0, AV_BPRINT_SIZE_UNLIMITED); while (!ff_text_eof(&tr)) { AVPacket *sub; const int64_t pos = ff_text_pos(&tr) - (c != 0); int is_sync, n = ff_smil_extract_next_text_chunk(&tr, &buf, &c); if (n == 0) break; is_sync = !av_strncasecmp(buf.str, "<SYNC", 5); if (is_sync) got_first_sync_point = 1; if (!got_first_sync_point) { av_bprintf(&hdr_buf, "%s", buf.str); } else { sub = ff_subtitles_queue_insert(&sami->q, buf.str, buf.len, !is_sync); if (!sub) { res = AVERROR(ENOMEM); goto end; } if (is_sync) { const char *p = ff_smil_get_attr_ptr(buf.str, "Start"); sub->pos = pos; sub->pts = p ? strtol(p, NULL, 10) : 0; sub->duration = -1; } } av_bprint_clear(&buf); } res = avpriv_bprint_to_extradata(st->codec, &hdr_buf); if (res < 0) goto end; ff_subtitles_queue_finalize(&sami->q); end: av_bprint_finalize(&buf, NULL); return res; }
static int64_t get_line(AVBPrint *buf, AVIOContext *pb) { int64_t pos = avio_tell(pb); av_bprint_clear(buf); for (;;) { char c = avio_r8(pb); if (!c) break; av_bprint_chars(buf, c, 1); if (c == '\n') break; } return pos; }
static int64_t get_line(AVBPrint *buf, FFTextReader *tr) { int64_t pos = ff_text_pos(tr); av_bprint_clear(buf); for (;;) { char c = ff_text_r8(tr); if (!c) break; av_bprint_chars(buf, c, 1); if (c == '\n') break; } return pos; }
static int microdvd_init(AVCodecContext *avctx) { int i, sidx; AVBPrint font_buf; int font_size = ASS_DEFAULT_FONT_SIZE; int color = ASS_DEFAULT_COLOR; int bold = ASS_DEFAULT_BOLD; int italic = ASS_DEFAULT_ITALIC; int underline = ASS_DEFAULT_UNDERLINE; int alignment = ASS_DEFAULT_ALIGNMENT; struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}}; av_bprint_init(&font_buf, 0, AV_BPRINT_SIZE_AUTOMATIC); av_bprintf(&font_buf, "%s", ASS_DEFAULT_FONT); if (avctx->extradata) { microdvd_load_tags(tags, avctx->extradata); for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) { switch (av_tolower(tags[i].key)) { case 'y': for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++) { if (tags[i].data1 & (1 << sidx)) { switch (MICRODVD_STYLES[sidx]) { case 'i': italic = 1; break; case 'b': bold = 1; break; case 'u': underline = 1; break; } } } break; case 'c': color = tags[i].data1; break; case 's': font_size = tags[i].data1; break; case 'p': alignment = 8; break; case 'f': av_bprint_clear(&font_buf); av_bprintf(&font_buf, "%.*s", tags[i].data_string_len, tags[i].data_string); break; } } } return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color, ASS_DEFAULT_BACK_COLOR, bold, italic, underline, ASS_DEFAULT_BORDERSTYLE, alignment); }
static int64_t read_line(AVBPrint *buf, AVIOContext *pb) { int64_t pos = avio_tell(pb); av_bprint_clear(buf); while(!url_feof(pb)) { int c = avio_r8(pb); if(c != '\r') { av_bprint_chars(buf, c, 1); } if(c == '\n') { break; } } return pos; }
static void read_chunk(AVIOContext *pb, AVBPrint *buf) { char eol_buf[5]; int n = 0, i = 0, nb_eol = 0; av_bprint_clear(buf); for (;;) { char c = avio_r8(pb); if (!c) break; /* ignore all initial line breaks */ if (n == 0 && is_eol(c)) continue; /* line break buffering: we don't want to add the trailing \r\n */ if (is_eol(c)) { nb_eol += c == '\n'; if (nb_eol == 2) break; eol_buf[i++] = c; if (i == sizeof(eol_buf) - 1) break; continue; } /* only one line break followed by data: we flush the line breaks * buffer */ if (i) { eol_buf[i] = 0; av_bprintf(buf, "%s", eol_buf); i = nb_eol = 0; } av_bprint_chars(buf, c, 1); n++; } /* FIXME: remove the following when the lavc SubRip decoder is fixed * (trailing tags are not correctly flushed, see what happens to FATE when * you disable this code) */ if (buf->len) av_bprintf(buf, "\n"); }
void ff_subtitles_read_chunk(AVIOContext *pb, AVBPrint *buf) { char eol_buf[5]; int n = 0, i = 0, nb_eol = 0; av_bprint_clear(buf); for (;;) { char c = avio_r8(pb); if (!c) break; /* ignore all initial line breaks */ if (n == 0 && is_eol(c)) continue; /* line break buffering: we don't want to add the trailing \r\n */ if (is_eol(c)) { nb_eol += c == '\n'; if (nb_eol == 2) break; eol_buf[i++] = c; if (i == sizeof(eol_buf) - 1) break; continue; } /* only one line break followed by data: we flush the line breaks * buffer */ if (i) { eol_buf[i] = 0; av_bprintf(buf, "%s", eol_buf); i = nb_eol = 0; } av_bprint_chars(buf, c, 1); n++; } }
static int expand_text(AVFilterContext *ctx, char *text, AVBPrint *bp) { int ret; av_bprint_clear(bp); while (*text) { if (*text == '\\' && text[1]) { av_bprint_chars(bp, text[1], 1); text += 2; } else if (*text == '%') { text++; if ((ret = expand_function(ctx, bp, &text)) < 0) return ret; } else { av_bprint_chars(bp, *text, 1); text++; } } if (!av_bprint_is_complete(bp)) return AVERROR(ENOMEM); return 0; }
static int realtext_read_header(AVFormatContext *s) { RealTextContext *rt = s->priv_data; AVStream *st = avformat_new_stream(s, NULL); AVBPrint buf; char c = 0; int res = 0, duration = read_ts("60"); // default duration is 60 seconds 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_REALTEXT; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); while (!url_feof(s->pb)) { AVPacket *sub; const int64_t pos = avio_tell(s->pb) - (c != 0); int n = ff_smil_extract_next_chunk(s->pb, &buf, &c); if (n == 0) break; if (!av_strncasecmp(buf.str, "<window", 7)) { /* save header to extradata */ const char *p = ff_smil_get_attr_ptr(buf.str, "duration"); if (p) duration = read_ts(p); st->codec->extradata = av_strdup(buf.str); if (!st->codec->extradata) { res = AVERROR(ENOMEM); goto end; } st->codec->extradata_size = buf.len + 1; } else { /* if we just read a <time> tag, introduce a new event, otherwise merge * with the previous one */ int merge = !av_strncasecmp(buf.str, "<time", 5) ? 0 : 1; sub = ff_subtitles_queue_insert(&rt->q, buf.str, buf.len, merge); if (!sub) { res = AVERROR(ENOMEM); goto end; } if (!merge) { const char *begin = ff_smil_get_attr_ptr(buf.str, "begin"); const char *end = ff_smil_get_attr_ptr(buf.str, "end"); sub->pos = pos; sub->pts = begin ? read_ts(begin) : 0; sub->duration = end ? (read_ts(end) - sub->pts) : duration; } } av_bprint_clear(&buf); } ff_subtitles_queue_finalize(&rt->q); end: av_bprint_finalize(&buf, NULL); return res; }
static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, int bufsize, const AVSubtitle *sub) { MovTextContext *s = avctx->priv_data; ASSDialog *dialog; int i, j, num, length; s->text_pos = 0; s->count = 0; s->style_box_flag = 0; s->style_entries = 0; for (i = 0; i < sub->num_rects; i++) { if (sub->rects[i]->type != SUBTITLE_ASS) { av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); return AVERROR(ENOSYS); } dialog = ff_ass_split_dialog(s->ass_ctx, sub->rects[i]->ass, 0, &num); for (; dialog && num--; dialog++) { ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); } if (s->style_box_flag) { s->tsmb_size = s->count * STYLE_RECORD_SIZE + SIZE_ADD; //size of one style record is 12 bytes s->tsmb_size = AV_RB32(&s->tsmb_size); s->tsmb_type = MKTAG('s','t','y','l'); s->style_entries = AV_RB16(&s->count); s->style_fontID = 0x00 | 0x01<<8; s->style_fontsize = 0x12; s->style_color = MKTAG(0xFF, 0xFF, 0xFF, 0xFF); /*The above three attributes are hard coded for now but will come from ASS style in the future*/ av_bprint_append_any(&s->buffer, &s->tsmb_size, 4); av_bprint_append_any(&s->buffer, &s->tsmb_type, 4); av_bprint_append_any(&s->buffer, &s->style_entries, 2); for (j = 0; j < s->count; j++) { av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_start, 2); av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_end, 2); av_bprint_append_any(&s->buffer, &s->style_fontID, 2); av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_flag, 1); av_bprint_append_any(&s->buffer, &s->style_fontsize, 1); av_bprint_append_any(&s->buffer, &s->style_color, 4); } for (j = 0; j < s->count; j++) { av_freep(&s->style_attributes[j]); } av_freep(&s->style_attributes); av_freep(&s->style_attributes_temp); } } AV_WB16(buf, s->text_pos); buf += 2; if (!av_bprint_is_complete(&s->buffer)) { length = AVERROR(ENOMEM); goto exit; } if (!s->buffer.len) { length = 0; goto exit; } if (s->buffer.len > bufsize - 3) { av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); length = AVERROR(EINVAL); goto exit; } memcpy(buf, s->buffer.str, s->buffer.len); length = s->buffer.len + 2; exit: av_bprint_clear(&s->buffer); return length; }
static void mov_text_style_cb(void *priv, const char style, int close) { MovTextContext *s = priv; if (!close) { if (s->style_box_flag == 0) { //first style entry s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp)); if (!s->style_attributes_temp) { av_bprint_clear(&s->buffer); s->style_box_flag = 0; return; } s->style_attributes_temp->style_flag = 0; s->style_attributes_temp->style_start = AV_RB16(&s->text_pos); } else { if (s->style_attributes_temp->style_flag) { //break the style record here and start a new one s->style_attributes_temp->style_end = AV_RB16(&s->text_pos); av_dynarray_add(&s->style_attributes, &s->count, s->style_attributes_temp); s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp)); if (!s->style_attributes_temp) { av_bprint_clear(&s->buffer); s->style_box_flag = 0; return; } s->style_attributes_temp->style_flag = s->style_attributes[s->count - 1]->style_flag; s->style_attributes_temp->style_start = AV_RB16(&s->text_pos); } else { s->style_attributes_temp->style_flag = 0; s->style_attributes_temp->style_start = AV_RB16(&s->text_pos); } } switch (style){ case 'b': s->style_attributes_temp->style_flag |= STYLE_FLAG_BOLD; break; case 'i': s->style_attributes_temp->style_flag |= STYLE_FLAG_ITALIC; break; case 'u': s->style_attributes_temp->style_flag |= STYLE_FLAG_UNDERLINE; break; } } else { s->style_attributes_temp->style_end = AV_RB16(&s->text_pos); av_dynarray_add(&s->style_attributes, &s->count, s->style_attributes_temp); s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp)); if (!s->style_attributes_temp) { av_bprint_clear(&s->buffer); s->style_box_flag = 0; return; } s->style_attributes_temp->style_flag = s->style_attributes[s->count - 1]->style_flag; switch (style){ case 'b': s->style_attributes_temp->style_flag &= ~STYLE_FLAG_BOLD; break; case 'i': s->style_attributes_temp->style_flag &= ~STYLE_FLAG_ITALIC; break; case 'u': s->style_attributes_temp->style_flag &= ~STYLE_FLAG_UNDERLINE; break; } if (s->style_attributes_temp->style_flag) { //start of new style record s->style_attributes_temp->style_start = AV_RB16(&s->text_pos); } } s->style_box_flag = 1; }
static int sami_paragraph_to_ass(AVCodecContext *avctx, const char *src) { SAMIContext *sami = avctx->priv_data; int ret = 0; char *tag = NULL; char *dupsrc = av_strdup(src); char *p = dupsrc; av_bprint_clear(&sami->content); for (;;) { char *saveptr = NULL; int prev_chr_is_space = 0; AVBPrint *dst = &sami->content; /* parse & extract paragraph tag */ p = av_stristr(p, "<P"); if (!p) break; if (p[2] != '>' && !av_isspace(p[2])) { // avoid confusion with tags such as <PRE> p++; continue; } if (dst->len) // add a separator with the previous paragraph if there was one av_bprintf(dst, "\\N"); tag = av_strtok(p, ">", &saveptr); if (!tag || !saveptr) break; p = saveptr; /* check if the current paragraph is the "source" (speaker name) */ if (av_stristr(tag, "ID=Source") || av_stristr(tag, "ID=\"Source\"")) { dst = &sami->source; av_bprint_clear(dst); } /* if empty event -> skip subtitle */ while (av_isspace(*p)) p++; if (!strncmp(p, " ", 6)) { ret = -1; goto end; } /* extract the text, stripping most of the tags */ while (*p) { if (*p == '<') { if (!av_strncasecmp(p, "<P", 2) && (p[2] == '>' || av_isspace(p[2]))) break; if (!av_strncasecmp(p, "<BR", 3)) av_bprintf(dst, "\\N"); p++; while (*p && *p != '>') p++; if (!*p) break; if (*p == '>') p++; } if (!av_isspace(*p)) av_bprint_chars(dst, *p, 1); else if (!prev_chr_is_space) av_bprint_chars(dst, ' ', 1); prev_chr_is_space = av_isspace(*p); p++; } } av_bprint_clear(&sami->full); if (sami->source.len) av_bprintf(&sami->full, "{\\i1}%s{\\i0}\\N", sami->source.str); av_bprintf(&sami->full, "%s", sami->content.str); end: av_free(dupsrc); return ret; }