static void fill_plaintext(struct sd *sd, double pts) { struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->shadow_track; ass_flush_events(track); char *text = get_text(sd, pts); if (!text) return; bstr dst = {0}; while (*text) { if (*text == '{') bstr_xappend(NULL, &dst, bstr0("\\")); bstr_xappend(NULL, &dst, (bstr){text, 1}); // Break ASS escapes with U+2060 WORD JOINER if (*text == '\\') mp_append_utf8_bstr(NULL, &dst, 0x2060); text++; } if (!dst.start || !dst.start[0]) return; int n = ass_alloc_event(track); ASS_Event *event = track->events + n; event->Start = 0; event->Duration = INT_MAX; event->Style = track->default_style; event->Text = strdup(dst.start); if (track->default_style < track->n_styles) track->styles[track->default_style].Alignment = ctx->on_top ? 6 : 2; }
static void decode(struct sd *sd, struct demux_packet *packet) { struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->ass_track; long long ipts = packet->pts * 1000 + 0.5; long long iduration = packet->duration * 1000 + 0.5; if (strcmp(sd->codec, "ass") == 0) { ass_process_chunk(track, packet->buffer, packet->len, ipts, iduration); return; } else if (strcmp(sd->codec, "ssa") == 0) { // broken ffmpeg ASS packet format ctx->flush_on_seek = true; ass_process_data(track, packet->buffer, packet->len); return; } // plaintext subs if (packet->pts == MP_NOPTS_VALUE) { MP_WARN(sd, "Subtitle without pts, ignored\n"); return; } if (ctx->extend_event >= 0 && ctx->extend_event < track->n_events) { ASS_Event *event = &track->events[ctx->extend_event]; if (event->Start <= ipts) event->Duration = ipts - event->Start; ctx->extend_event = -1; } unsigned char *text = packet->buffer; if (!sd->no_remove_duplicates) { for (int i = 0; i < track->n_events; i++) { if (track->events[i].Start == ipts && (track->events[i].Duration == iduration) && strcmp(track->events[i].Text, text) == 0) return; // We've already added this subtitle } } int eid = ass_alloc_event(track); ASS_Event *event = track->events + eid; if (packet->duration == 0) { MP_WARN(sd, "Subtitle without duration or " "duration set to 0 at pts %f.\n", packet->pts); } if (packet->duration < 0) { // Assume unknown duration. The FFmpeg API is very unclear about this. MP_WARN(sd, "Assuming subtitle without duration at pts %f\n", packet->pts); // _If_ there's a next subtitle, the duration will be adjusted again. // If not, show it forever. iduration = INT_MAX; ctx->extend_event = eid; } event->Start = ipts; event->Duration = iduration; event->Style = track->default_style; event->Text = strdup(text); }
static void decode(struct sh_sub *sh, struct osd_state *osd, void *data, int data_len, double pts, double duration) { unsigned char *text = data; struct sd_ass_priv *ctx = sh->context; ASS_Track *track = ctx->ass_track; if (sh->type == 'a') { // ssa/ass subs ass_process_chunk(track, data, data_len, (long long)(pts*1000 + 0.5), (long long)(duration*1000 + 0.5)); return; } // plaintext subs if (pts == MP_NOPTS_VALUE) { mp_msg(MSGT_SUBREADER, MSGL_WARN, "Subtitle without pts, ignored\n"); return; } long long ipts = pts * 1000 + 0.5; long long iduration = duration * 1000 + 0.5; if (ctx->incomplete_event) { ctx->incomplete_event = false; ASS_Event *event = track->events + track->n_events - 1; if (ipts <= event->Start) free_last_event(track); else event->Duration = ipts - event->Start; } // Note: we rely on there being guaranteed 0 bytes after data packets int len = strlen(text); if (len < 5) { // Some tracks use a whitespace (but not empty) packet to mark end // of previous subtitle. for (int i = 0; i < len; i++) if (!strchr(" \f\n\r\t\v", text[i])) goto not_all_whitespace; return; } not_all_whitespace:; char buf[500]; subassconvert_subrip(text, buf, sizeof(buf)); for (int i = 0; i < track->n_events; i++) if (track->events[i].Start == ipts && (duration <= 0 || track->events[i].Duration == iduration) && strcmp(track->events[i].Text, buf) == 0) return; // We've already added this subtitle if (duration <= 0) { iduration = 10000; ctx->incomplete_event = true; } int eid = ass_alloc_event(track); ASS_Event *event = track->events + eid; event->Start = ipts; event->Duration = iduration; event->Style = track->default_style; event->Text = strdup(buf); }
void LibASS::addASSEvent(const QByteArray &text, double Start, double Duration) { if (!ass_sub_track || !ass_sub_renderer || text.isEmpty() || Start < 0 || Duration < 0) return; int eventID = ass_alloc_event(ass_sub_track); ASS_Event *event = &ass_sub_track->events[eventID]; event->Text = strdup(text.data()); event->Start = Start * 1000; event->Duration = Duration * 1000; event->Style = 0; event->ReadOrder = eventID; }
/** * \brief Convert subtitle to ASS_Events for the given track * \param track track * \param sub subtitle to convert * \return event id * note: assumes that subtitle is _not_ fps-based; caller must manually correct * Start and Duration in other case. **/ static int ass_process_subtitle(ASS_Track *track, subtitle *sub) { int eid; ASS_Event *event; int len = 0, j; char *p; char *end; eid = ass_alloc_event(track); event = track->events + eid; event->Start = sub->start * 10; event->Duration = (sub->end - sub->start) * 10; event->Style = 0; for (j = 0; j < sub->lines; ++j) len += sub->text[j] ? strlen(sub->text[j]) : 0; len += 2 * sub->lines; // '\N', including the one after the last line len += 6; // {\anX} len += 1; // '\0' event->Text = malloc(len); end = event->Text + len; p = event->Text; if (sub->alignment) p += snprintf(p, end - p, "{\\an%d}", sub->alignment); for (j = 0; j < sub->lines; ++j) p += snprintf(p, end - p, "%s\\N", sub->text[j]); if (sub->lines > 0) p -= 2; // remove last "\N" *p = 0; if (check_duplicate_plaintext_event(track)) { ass_free_event(track, eid); track->n_events--; return -1; } mp_msg(MSGT_ASS, MSGL_V, "plaintext event at %" PRId64 ", +%" PRId64 ": %s \n", (int64_t) event->Start, (int64_t) event->Duration, event->Text); return eid; }
static void decode(struct sd *sd, struct demux_packet *packet) { struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->ass_track; long long ipts = packet->pts * 1000 + 0.5; long long iduration = packet->duration * 1000 + 0.5; if (strcmp(sd->codec, "ass") == 0) { ass_process_chunk(track, packet->buffer, packet->len, ipts, iduration); return; } else if (strcmp(sd->codec, "ssa") == 0) { // broken ffmpeg ASS packet format ctx->flush_on_seek = true; ass_process_data(track, packet->buffer, packet->len); return; } // plaintext subs if (packet->pts == MP_NOPTS_VALUE) { mp_msg(MSGT_SUBREADER, MSGL_WARN, "Subtitle without pts, ignored\n"); return; } if (packet->duration <= 0) { mp_msg(MSGT_SUBREADER, MSGL_WARN, "Subtitle without duration or " "duration set to 0 at pts %f, ignored\n", packet->pts); return; } unsigned char *text = packet->buffer; if (!sd->no_remove_duplicates) { for (int i = 0; i < track->n_events; i++) { if (track->events[i].Start == ipts && (track->events[i].Duration == iduration) && strcmp(track->events[i].Text, text) == 0) return; // We've already added this subtitle } } int eid = ass_alloc_event(track); ASS_Event *event = track->events + eid; event->Start = ipts; event->Duration = iduration; event->Style = track->default_style; event->Text = strdup(text); }
void LibASS::initOSD() { if (osd_track && osd_style && osd_event && osd_renderer) return; osd_track = ass_new_track(ass); int styleID = ass_alloc_style(osd_track); osd_style = &osd_track->styles[styleID]; setOSDStyle(); int eventID = ass_alloc_event(osd_track); osd_event = &osd_track->events[eventID]; osd_event->Start = 0; osd_event->Duration = 1; osd_event->Style = styleID; osd_event->ReadOrder = eventID; osd_renderer = ass_renderer_init(ass); ass_set_fonts(osd_renderer, NULL, NULL, 1, NULL, 1); }