| @@ -51,7 +51,7 @@ struct sd_ass_priv { · 50 unmodified lines |
| 51 | 51 | bool clear_once; |
| 52 | 52 | struct mp_ass_packer *packer; |
| 53 | 53 | struct sub_bitmap_copy_cache *copy_cache; |
| 54 | | − char last_text[500]; |
| 54 | + bstr last_text; |
| 55 | 55 | struct mp_image_params video_params; |
| 56 | 56 | struct mp_image_params last_params; |
| 57 | 57 | struct mp_osd_res osd; |
| @@ -709,30 +709,23 @@ done: · 651 unmodified lines |
| 709 | 709 | return res; |
| 710 | 710 | } |
| 711 | 711 | |
| 712 | | −struct buf { |
| 713 | | − char *start; |
| 714 | | − int size; |
| 715 | | − int len; |
| 716 | | −}; |
| 712 | +#define MAX_BUF_SIZE 1024 * 1024 |
| 713 | +#define MIN_EXPAND_SIZE 4096 |
| 717 | 714 | |
| 718 | | −static void append(struct buf *b, char c) |
| 715 | +static void append(bstr *b, char c) |
| 719 | 716 | { |
| 720 | | − if (b->len < b->size) { |
| 721 | | − b->start[b->len] = c; |
| 722 | | − b->len++; |
| 723 | | − } |
| 717 | + bstr_xappend(NULL, b, (bstr){&c, 1}); |
| 724 | 718 | } |
| 725 | 719 | |
| 726 | | −static void ass_to_plaintext(struct buf *b, const char *in) |
| 720 | +static void ass_to_plaintext(bstr *b, const char *in) |
| 727 | 721 | { |
| 728 | | − bool in_tag = false; |
| 729 | 722 | const char *open_tag_pos = NULL; |
| 730 | 723 | bool in_drawing = false; |
| 731 | 724 | while (*in) { |
| 732 | | − if (in_tag) { |
| 725 | + if (open_tag_pos) { |
| 733 | 726 | if (in[0] == '}') { |
| 734 | 727 | in += 1; |
| 735 | | − in_tag = false; |
| 728 | + open_tag_pos = NULL; |
| 736 | 729 | } else if (in[0] == '\\' && in[1] == 'p' && in[2] != 'o') { |
| 737 | 730 | in += 2; |
| 738 | 731 | // Skip text between \pN and \p0 tags. A \p without a number |
| @@ -756,7 +749,6 @@ static void ass_to_plaintext(struct buf *b, const char *in) · 17 unmodified lines |
| 756 | 749 | } else if (in[0] == '{') { |
| 757 | 750 | open_tag_pos = in; |
| 758 | 751 | in += 1; |
| 759 | | − in_tag = true; |
| 760 | 752 | } else { |
| 761 | 753 | if (!in_drawing) |
| 762 | 754 | append(b, in[0]); |
| @@ -765,65 +757,64 @@ static void ass_to_plaintext(struct buf *b, const char *in) · 2 unmodified lines |
| 765 | 757 | } |
| 766 | 758 | } |
| 767 | 759 | // A '{' without a closing '}' is always visible. |
| 768 | | − if (in_tag) { |
| 769 | | − while (*open_tag_pos) |
| 770 | | − append(b, *open_tag_pos++); |
| 760 | + if (open_tag_pos) { |
| 761 | + bstr_xappend(NULL, b, bstr0(open_tag_pos)); |
| 771 | 762 | } |
| 772 | 763 | } |
| 773 | 764 | |
| 774 | | −// Empty string counts as whitespace. Reads s[len-1] even if there are \0s. |
| 775 | | −static bool is_whitespace_only(char *s, int len) |
| 765 | +// Empty string counts as whitespace. |
| 766 | +static bool is_whitespace_only(bstr b) |
| 776 | 767 | { |
| 777 | | − for (int n = 0; n < len; n++) { |
| 778 | | − if (s[n] != ' ' && s[n] != '\t') |
| 768 | + for (int n = 0; n < b.len; n++) { |
| 769 | + if (b.start[n] != ' ' && b.start[n] != '\t') |
| 779 | 770 | return false; |
| 780 | 771 | } |
| 781 | 772 | return true; |
| 782 | 773 | } |
| 783 | 774 | |
| 784 | | −static char *get_text_buf(struct sd *sd, double pts, enum sd_text_type type) |
| 775 | +static bstr get_text_buf(struct sd *sd, double pts, enum sd_text_type type) |
| 785 | 776 | { |
| 786 | 777 | struct sd_ass_priv *ctx = sd->priv; |
| 787 | 778 | ASS_Track *track = ctx->ass_track; |
| 788 | 779 | |
| 789 | 780 | if (pts == MP_NOPTS_VALUE) |
| 790 | | − return NULL; |
| 781 | + return (bstr){0}; |
| 791 | 782 | long long ipts = find_timestamp(sd, pts); |
| 792 | 783 | |
| 793 | | − struct buf b = {ctx->last_text, sizeof(ctx->last_text) - 1}; |
| 784 | + bstr *b = &ctx->last_text; |
| 785 | + |
| 786 | + if (!b->start) |
| 787 | + b->start = talloc_size(ctx, 4096); |
| 788 | + |
| 789 | + b->len = 0; |
| 794 | 790 | |
| 795 | 791 | for (int i = 0; i < track->n_events; ++i) { |
| 796 | 792 | ASS_Event *event = track->events + i; |
| 797 | 793 | if (ipts >= event->Start && ipts < event->Start + event->Duration) { |
| 798 | 794 | if (event->Text) { |
| 799 | | − int start = b.len; |
| 795 | + int start = b->len; |
| 800 | 796 | if (type == SD_TEXT_TYPE_PLAIN) { |
| 801 | | − ass_to_plaintext(&b, event->Text); |
| 797 | + ass_to_plaintext(b, event->Text); |
| 802 | 798 | } else { |
| 803 | | − char *t = event->Text; |
| 804 | | − while (*t) |
| 805 | | − append(&b, *t++); |
| 799 | + bstr_xappend(NULL, b, bstr0(event->Text)); |
| 806 | 800 | } |
| 807 | | − if (is_whitespace_only(&b.start[start], b.len - start)) { |
| 808 | | − b.len = start; |
| 801 | + if (is_whitespace_only(bstr_cut(*b, start))) { |
| 802 | + b->len = start; |
| 809 | 803 | } else { |
| 810 | | − append(&b, '\n'); |
| 804 | + append(b, '\n'); |
| 811 | 805 | } |
| 812 | 806 | } |
| 813 | 807 | } |
| 814 | 808 | } |
| 815 | 809 | |
| 816 | | − b.start[b.len] = '\0'; |
| 817 | | − |
| 818 | | − if (b.len > 0 && b.start[b.len - 1] == '\n') |
| 819 | | − b.start[b.len - 1] = '\0'; |
| 810 | + bstr_eatend(b, (bstr)bstr0_lit("\n")); |
| 820 | 811 | |
| 821 | | − return ctx->last_text; |
| 812 | + return *b; |
| 822 | 813 | } |
| 823 | 814 | |
| 824 | 815 | static char *get_text(struct sd *sd, double pts, enum sd_text_type type) |
| 825 | 816 | { |
| 826 | | − return talloc_strdup(NULL, get_text_buf(sd, pts, type)); |
| 817 | + return bstrto0(NULL, get_text_buf(sd, pts, type)); |
| 827 | 818 | } |
| 828 | 819 | |
| 829 | 820 | static struct sd_times get_times(struct sd *sd, double pts) |
| @@ -862,20 +853,26 @@ static void fill_plaintext(struct sd *sd, double pts) · 32 unmodified lines |
| 862 | 853 | |
| 863 | 854 | ass_flush_events(track); |
| 864 | 855 | |
| 865 | | − char *text = get_text_buf(sd, pts, SD_TEXT_TYPE_PLAIN); |
| 866 | | − if (!text) |
| 856 | + bstr text = get_text_buf(sd, pts, SD_TEXT_TYPE_PLAIN); |
| 857 | + if (!text.len) |
| 867 | 858 | return; |
| 868 | 859 | |
| 869 | 860 | bstr dst = {0}; |
| 870 | 861 | |
| 871 | | − while (*text) { |
| 872 | | − if (*text == '{') |
| 862 | + while (text.len) { |
| 863 | + if (*text.start == '{') { |
| 864 | + bstr_xappend(NULL, &dst, bstr0("\\{")); |
| 865 | + text = bstr_cut(text, 1); |
| 866 | + } else if (*text.start == '\\') { |
| 873 | 867 | bstr_xappend(NULL, &dst, bstr0("\\")); |
| 874 | | − bstr_xappend(NULL, &dst, (bstr){text, 1}); |
| 875 | | − // Break ASS escapes with U+2060 WORD JOINER |
| 876 | | − if (*text == '\\') |
| 868 | + // Break ASS escapes with U+2060 WORD JOINER |
| 877 | 869 | mp_append_utf8_bstr(NULL, &dst, 0x2060); |
| 878 | | − text++; |
| 870 | + text = bstr_cut(text, 1); |
| 871 | + } |
| 872 | + |
| 873 | + int i = bstrcspn(text, "{\\"); |
| 874 | + bstr_xappend(NULL, &dst, (bstr){text.start, i}); |
| 875 | + text = bstr_cut(text, i); |
| 879 | 876 | } |
| 880 | 877 | |
| 881 | 878 | if (!dst.start) |
| @@ -1103,11 +1100,10 @@ bstr sd_ass_pkt_text(struct sd_filter *ft, struct demux_packet *pkt, int offset) · 221 unmodified lines |
| 1103 | 1100 | return txt; |
| 1104 | 1101 | } |
| 1105 | 1102 | |
| 1106 | | −bstr sd_ass_to_plaintext(char *out, size_t out_siz, const char *in) |
| 1103 | +bstr sd_ass_to_plaintext(char **out, const char *in) |
| 1107 | 1104 | { |
| 1108 | | − struct buf b = {out, out_siz, 0}; |
| 1105 | + bstr b = {*out}; |
| 1109 | 1106 | ass_to_plaintext(&b, in); |
| 1110 | | − if (b.len < out_siz) |
| 1111 | | − out[b.len] = 0; |
| 1112 | | − return (bstr){out, b.len}; |
| 1107 | + *out = b.start; |
| 1108 | + return b; |
| 1113 | 1109 | } |