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