From 124324a6bab409b24df6c1a19d829a1da15f0268 Mon Sep 17 00:00:00 2001 From: computerfan Date: Sun, 31 May 2020 23:25:55 +0800 Subject: [PATCH 01/10] Fix \$va: body part of outline should not be drawn when va is applied --- src/subtitles/RTS.cpp | 6 ++++-- src/subtitles/STS.cpp | 1 + src/subtitles/STS.h | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/subtitles/RTS.cpp b/src/subtitles/RTS.cpp index be5dfca..1dc75f6 100644 --- a/src/subtitles/RTS.cpp +++ b/src/subtitles/RTS.cpp @@ -1118,7 +1118,7 @@ CRect CLine::PaintOutline(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CP if(w->m_style.borderStyle == 0) { #ifdef _VSMOD // patch m004. gradient colors - bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, !w->m_style.alpha[0] && !w->m_style.alpha[1] && !alpha, true, 2, w->m_style.mod_grad, mod_vc); + bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, !w->m_style.alpha[0] && !w->m_style.alpha[1] && !alpha && !w->m_style.mod_grad.bodyIsGradAlpha, true, 2, w->m_style.mod_grad, mod_vc); #else bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, !w->m_style.alpha[0] && !w->m_style.alpha[1] && !alpha, true); #endif @@ -2235,8 +2235,10 @@ bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, CStringW str, STSStyle& //if (!fAnimate) style.mod_grad.mode[i] = 1; - if (i == 0 || i == 1) + if (i == 0 || i == 1) { style.mod_grad.mode[0] = style.mod_grad.mode[1] = 1; + style.mod_grad.bodyIsGradAlpha = true; + } } } #endif diff --git a/src/subtitles/STS.cpp b/src/subtitles/STS.cpp index 5a0d755..27e8405 100644 --- a/src/subtitles/STS.cpp +++ b/src/subtitles/STS.cpp @@ -3804,6 +3804,7 @@ void MOD_GRADIENT::clear() subpixx = 0; subpixy = 0; fadalpha = 0xFF; + bodyIsGradAlpha = false; } #include diff --git a/src/subtitles/STS.h b/src/subtitles/STS.h index 451f328..c3474d7 100644 --- a/src/subtitles/STS.h +++ b/src/subtitles/STS.h @@ -82,6 +82,7 @@ class MOD_GRADIENT COLORREF alphas[4]; // a COLORREF color[4][4]; // vc (rgb is reverted to style.colors) BYTE alpha[4][4]; // va + bool bodyIsGradAlpha; int mode[4]; // for renderer From 7f4e31e0cf3c9056d0691c69ae2943592e4ef6f3 Mon Sep 17 00:00:00 2001 From: computerfan Date: Mon, 12 Oct 2020 21:55:09 +0800 Subject: [PATCH 02/10] Fix repeated scaling of opaque box --- src/subtitles/RTS.cpp | 7 ++++--- src/subtitles/RTS.h | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/subtitles/RTS.cpp b/src/subtitles/RTS.cpp index 1dc75f6..79c4edc 100644 --- a/src/subtitles/RTS.cpp +++ b/src/subtitles/RTS.cpp @@ -75,7 +75,7 @@ CWord::CWord(STSStyle& style, CStringW str, int ktype, int kstart, int kend, dou , m_scalex(scalex), m_scaley(scaley) , m_fDrawn(false), m_p(INT_MAX, INT_MAX) , m_fLineBreak(false), m_fWhiteSpaceChar(false) - , m_pOpaqueBox(NULL) + , m_pOpaqueBox(NULL), isOpaqueBox(false) { if(str.IsEmpty()) { @@ -151,8 +151,8 @@ void CWord::Paint(CPoint p, CPoint org) void CWord::Transform(CPoint org) { - double scalex = m_style.fontScaleX / 100; - double scaley = m_style.fontScaleY / 100; + double scalex = isOpaqueBox ? 1 : m_style.fontScaleX / 100; + double scaley = isOpaqueBox ? 1 : m_style.fontScaleY / 100; const double xzoomf = m_scalex * 20000.0; const double yzoomf = m_scaley * 20000.0; @@ -534,6 +534,7 @@ bool CWord::CreateOpaqueBox() -w, m_ascent + m_descent + h); m_pOpaqueBox = DNew CPolygon(style, str, 0, 0, 0, 1.0 / 8, 1.0 / 8, 0); + m_pOpaqueBox->isOpaqueBox = true; return(!!m_pOpaqueBox); } diff --git a/src/subtitles/RTS.h b/src/subtitles/RTS.h index 81c1254..a7b2882 100644 --- a/src/subtitles/RTS.h +++ b/src/subtitles/RTS.h @@ -57,6 +57,8 @@ class CWord : public Rasterizer CPolygon* m_pOpaqueBox; + bool isOpaqueBox; + int m_ktype, m_kstart, m_kend; int m_width, m_ascent, m_descent; From 8531775cdd2e56504cb1d3e40e37ea16e7b84557 Mon Sep 17 00:00:00 2001 From: computerfan Date: Sat, 21 May 2022 01:32:37 +0100 Subject: [PATCH 03/10] Fix yoffset of $img texture being affected when using clip/iclip Previously, texture yoffset is affected by the bottom edge of a rectangular clip, which can cause unexpected textures when using rect clip tag, especially iclips, with $img tag. --- src/subtitles/Rasterizer.cpp | 3 +++ src/subtitles/STS.cpp | 1 + src/subtitles/STS.h | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/subtitles/Rasterizer.cpp b/src/subtitles/Rasterizer.cpp index 6a24ee5..360dc43 100644 --- a/src/subtitles/Rasterizer.cpp +++ b/src/subtitles/Rasterizer.cpp @@ -1781,6 +1781,8 @@ CRect Rasterizer::Draw(SubPicDesc& spd, CRect& clipRect, byte* pAlphaMask, int x int h = mOverlayHeight; int xo = 0, yo = 0; + int yBeforeClip = y; + // Again, limiting? if(x < r.left) { @@ -1841,6 +1843,7 @@ CRect Rasterizer::Draw(SubPicDesc& spd, CRect& clipRect, byte* pAlphaMask, int x rnfo.mod_grad.xoffset = xo; rnfo.mod_grad.yoffset = yo; rnfo.typ = typ; + rnfo.mod_grad.clipDiff = clipRect.bottom < yBeforeClip + mOverlayHeight ? yBeforeClip + mOverlayHeight - clipRect.bottom : 0; #else // The complex "vector clip mask" I think. rnfo.am = pAlphaMask + spd.w * y + x; diff --git a/src/subtitles/STS.cpp b/src/subtitles/STS.cpp index 27e8405..87cf420 100644 --- a/src/subtitles/STS.cpp +++ b/src/subtitles/STS.cpp @@ -3837,6 +3837,7 @@ DWORD MOD_GRADIENT::getmixcolor(int tx, int ty, int i) // too slow T.T // unwarp tx += b_images[i].xoffset; ty += b_images[i].yoffset; + ty += clipDiff; while(tx > b_images[i].width - 1) tx -= b_images[i].width; while(ty > b_images[i].height - 1) ty -= b_images[i].height; while(tx < 0) tx += b_images[i].width; diff --git a/src/subtitles/STS.h b/src/subtitles/STS.h index c3474d7..486141b 100644 --- a/src/subtitles/STS.h +++ b/src/subtitles/STS.h @@ -100,6 +100,8 @@ class MOD_GRADIENT // for background image MOD_PNGIMAGE b_images[4]; + int clipDiff; + MOD_GRADIENT(); bool operator == (MOD_GRADIENT& mg); From 01f1a1d90701880c3b152f8ab3104fa55457ec23 Mon Sep 17 00:00:00 2001 From: computerfan Date: Thu, 26 May 2022 16:50:43 +0100 Subject: [PATCH 04/10] Fix wrong LSB when working with 10/16 bit clips Fix computerfan/VSFilterMod#2 --- src/vsfilter/plugins.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vsfilter/plugins.cpp b/src/vsfilter/plugins.cpp index 556c39e..a4131a6 100644 --- a/src/vsfilter/plugins.cpp +++ b/src/vsfilter/plugins.cpp @@ -928,7 +928,7 @@ namespace VapourSynth { if (uintptr_t(src) & 0xf) sse2End = src; - __m128i lomask = _mm_set1_epi16(0xff00i16); + __m128i lomask = _mm_set1_epi16(0xffi16); while (src <= sse2End) { __m128i buf = _mm_load_si128((const __m128i*)src); From 808f492f2e6feb92c975cbed9faaa7b4272cfec5 Mon Sep 17 00:00:00 2001 From: computerfan Date: Sat, 18 Jun 2022 00:25:54 +0100 Subject: [PATCH 05/10] correctly handle luma values for full range yuv during alpha blending --- src/subpic/MemSubPic.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/subpic/MemSubPic.cpp b/src/subpic/MemSubPic.cpp index 8a48ccb..79dbe3a 100644 --- a/src/subpic/MemSubPic.cpp +++ b/src/subpic/MemSubPic.cpp @@ -573,7 +573,7 @@ STDMETHODIMP CMemSubPic::AlphaBlt(RECT* pSrc, RECT* pDst, SubPicDesc* pTarget) { if(s2[3] < 0xff) { - d2[0] = (((d2[0] - 0x10) * s2[3]) >> 8) + s2[1]; + d2[0] = (((d2[0] - RANGE[0][3]) * s2[3]) >> 8) + s2[1]; } } } @@ -640,7 +640,7 @@ STDMETHODIMP CMemSubPic::AlphaBlt(RECT* pSrc, RECT* pDst, SubPicDesc* pTarget) unsigned int ia = (s2[3] + s2[3+src.pitch] + is2[3] + is2[3+src.pitch]) >> 2; if(ia < 0xff) { - *d2 = (((*d2 - 0x80) * ia) >> 8) + ((s2[0] + s2[src.pitch]) >> 1); + *d2 = (((*d2 - RANGE[i+1][3]) * ia) >> 8) + ((s2[0] + s2[src.pitch]) >> 1); } } } From 5d4125f147b59afbb66cb615fb8b8d8b320631a2 Mon Sep 17 00:00:00 2001 From: Masaiki Date: Thu, 13 Apr 2023 20:57:42 +0800 Subject: [PATCH 06/10] fix: create RGB format buffer like YUV format, which fixes a crash caused by passing RGB24 format clip --- src/vsfilter/plugins.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vsfilter/plugins.cpp b/src/vsfilter/plugins.cpp index a4131a6..5e63e7d 100644 --- a/src/vsfilter/plugins.cpp +++ b/src/vsfilter/plugins.cpp @@ -1143,28 +1143,26 @@ namespace VapourSynth { class VSFRGBBuf : public VSFFrameBuf { - VSFrameRef* tmp; + std::unique_ptr buffer; const VSAPI* api; const VSFilterData* d; public: ~VSFRGBBuf() { - if (tmp) - api->freeFrame(tmp); } VSFRGBBuf(const VSAPI* api, VSCore *core, const VSFilterData* d, const VSFrameRef* frame) : api(api), d(d) { - tmp = api->newVideoFrame(api->getFormatPreset(pfCompatBGR32, core), d->vi->width, d->vi->height, nullptr, core); + buffer = std::make_unique(d->vi->width * d->vi->height * 4); const int srcStride = api->getStride(frame, 0); - const int tmpStride = api->getStride(tmp, 0); + const int tmpStride = d->vi->width*4; const uint8_t * srcpR = api->getReadPtr(frame, 0); const uint8_t * srcpG = api->getReadPtr(frame, 1); const uint8_t * srcpB = api->getReadPtr(frame, 2); - uint8_t * VS_RESTRICT tmpp = api->getWritePtr(tmp, 0); + uint8_t * VS_RESTRICT tmpp = buffer.get(); tmpp += tmpStride * (d->vi->height - 1); @@ -1185,16 +1183,16 @@ namespace VapourSynth { subpic.w = d->vi->width; subpic.h = d->vi->height; subpic.pitch = tmpStride; - subpic.bits = api->getWritePtr(tmp, 0); + subpic.bits = buffer.get(); subpic.bpp = 32; subpic.type = MSP_RGB32; } void WriteTo(VSFrameRef* frame) override { - const int tmpStride = api->getStride(tmp, 0); + const int tmpStride = d->vi->width * 4; const int dstStride = api->getStride(frame, 0); - const uint8_t * tmpp = api->getReadPtr(tmp, 0); + const uint8_t * tmpp = buffer.get(); uint8_t * VS_RESTRICT dstpR = api->getWritePtr(frame, 0); uint8_t * VS_RESTRICT dstpG = api->getWritePtr(frame, 1); uint8_t * VS_RESTRICT dstpB = api->getWritePtr(frame, 2); From c27e4a58fd208d4794a241056ee5bfaab10a7e67 Mon Sep 17 00:00:00 2001 From: Masaiki Date: Thu, 13 Apr 2023 21:00:25 +0800 Subject: [PATCH 07/10] format: tab to space in plugins.cpp --- src/vsfilter/plugins.cpp | 658 +++++++++++++++++++-------------------- 1 file changed, 329 insertions(+), 329 deletions(-) diff --git a/src/vsfilter/plugins.cpp b/src/vsfilter/plugins.cpp index 5e63e7d..32f80ea 100644 --- a/src/vsfilter/plugins.cpp +++ b/src/vsfilter/plugins.cpp @@ -888,200 +888,200 @@ namespace VapourSynth { vsapi->setVideoInfo(d->vi, 1, node); } - class VSFFrameBuf - { - protected: - VSFFrameBuf() + class VSFFrameBuf + { + protected: + VSFFrameBuf() : subpic{} , subpic2{} { } - public: - virtual ~VSFFrameBuf() {} - virtual void WriteTo(VSFrameRef* frame) = 0; - SubPicDesc subpic; + public: + virtual ~VSFFrameBuf() {} + virtual void WriteTo(VSFrameRef* frame) = 0; + SubPicDesc subpic; SubPicDesc subpic2; - private: - VSFFrameBuf(const VSFFrameBuf*) = delete; - VSFFrameBuf* operator = (const VSFFrameBuf*) = delete; - }; - - template - class VSFYUVBuf : public VSFFrameBuf - { - uint8_t* Buffer; - - uint8_t* BufDatas[3]; - uint8_t* BufDatas2[3]; - int BufStrides[3]; - - const VSAPI* api; - const VSFilterData* d; - - template - static inline void SplitBits(int sampleCount, const uint16_t* src, uint8_t* dst1, uint8_t* dst2) - { - auto srcEnd = src + sampleCount; - auto sse2End = src + sampleCount - 8; - if (uintptr_t(src) & 0xf) - sse2End = src; - - __m128i lomask = _mm_set1_epi16(0xffi16); - while (src <= sse2End) - { - __m128i buf = _mm_load_si128((const __m128i*)src); - __m128i hi, lo; - - if (BC == 16) - { - hi = _mm_srli_epi16(buf, 8); - hi = _mm_packus_epi16(hi, hi); - lo = _mm_and_si128(buf, lomask); - lo = _mm_packus_epi16(lo, lo); - } - else if (BC == 10) - { - hi = _mm_srli_epi16(buf, 2); - hi = _mm_packus_epi16(hi, hi); - lo = _mm_slli_epi16(buf, 6); - lo = _mm_and_si128(lo, lomask); - lo = _mm_packus_epi16(lo, lo); - } - _mm_storel_epi64((__m128i*)dst1, hi); - _mm_storel_epi64((__m128i*)dst2, lo); - dst1 += 8; - dst2 += 8; - src += 8; - } - - while (src < srcEnd) - { - if (BITDEPTH == 10) - { - *dst1 = ((*src) >> 2) & 0xff; - *dst2 = ((*src) << 6) & 0xff; - } - else if (BITDEPTH == 16) - { - *dst1 = ((*src) >> 8) & 0xff; - *dst2 = (*src) & 0xff; - } - - dst1 += 1; - dst2 += 1; - src += 1; - } - } - - template - static inline void MergeBits(int sampleCount, uint16_t* dst, const uint8_t* src1, const uint8_t* src2) - { - auto srcEnd = dst + sampleCount; - auto sse2End = dst + sampleCount - 8; - if (uintptr_t(dst) & 0xf) - sse2End = dst; - - while (dst < sse2End) - { - __m128i hbuf = _mm_loadl_epi64((const __m128i*) src1); - __m128i lbuf = _mm_loadl_epi64((const __m128i*) src2); - hbuf = _mm_unpacklo_epi8(hbuf, _mm_setzero_si128()); - hbuf = _mm_slli_epi16(hbuf, 8); - lbuf = _mm_unpacklo_epi8(lbuf, _mm_setzero_si128()); - hbuf = _mm_adds_epi16(hbuf, lbuf); - if (BC == 10) - hbuf = _mm_srli_epi16(hbuf, 6); - - _mm_store_si128((__m128i*)dst, hbuf); - src1 += 8; - src2 += 8; - dst += 8; - } - - while (dst < srcEnd) - { - *dst = (*src1 << 8) | *src2; - if (BC == 10) - *dst >>= 6; - src1 += 1; - src2 += 1; - dst += 1; - } - } - - public: - ~VSFYUVBuf() - { - if (Buffer) - free(Buffer); - } - - VSFYUVBuf(const VSAPI* api, VSCore *core, const VSFilterData* d, const VSFrameRef* frame) - : api(api), d(d) - { - int totalSize = 0; - for (int i = 0; i < 3; ++i) - { - BufStrides[i] = api->getStride(frame, i); - totalSize += BufStrides[i] * d->vi->height / (i == 0 ? 1 : 2); - } - Buffer = (uint8_t*)malloc(totalSize); - - if (BITDEPTH <= 8) - { - BufDatas[0] = Buffer; - BufDatas[1] = BufDatas[0] + BufStrides[0] * d->vi->height; - BufDatas[2] = BufDatas[1] + BufStrides[1] * d->vi->height / 2; - - for (int i = 0; i < 3; ++i) - { - const uint8_t* p = api->getReadPtr(frame, i); - memcpy(BufDatas[i], p, api->getStride(frame, i) * d->vi->height / (i == 0 ? 1 : 2)); - } - } - else - { - for (int i = 0; i < 3; ++i) - BufStrides[i] /= 2; - - BufDatas[0] = Buffer; - BufDatas[1] = BufDatas[0] + BufStrides[0] * d->vi->height; - BufDatas[2] = BufDatas[1] + BufStrides[1] * d->vi->height / 2; - BufDatas2[0] = BufDatas[2] + BufStrides[2] * d->vi->height / 2; - BufDatas2[1] = BufDatas2[0] + BufStrides[0] * d->vi->height; - BufDatas2[2] = BufDatas2[1] + BufStrides[1] * d->vi->height / 2; - - for (int i = 0; i < 3; ++i) - { - const uint8_t* p = api->getReadPtr(frame, i); - int srcStride = api->getStride(frame, i); - - int wEnd = d->vi->width / (i == 0 ? 1 : 2); - int hEnd = d->vi->height / (i == 0 ? 1 : 2); - uint8_t* pDst = BufDatas[i]; - uint8_t* pDst2 = BufDatas2[i]; - - for (int h = 0; h < hEnd; ++h) - { - const uint16_t* pSample = reinterpret_cast(p + h * srcStride); - uint8_t* pDstSample = BufDatas[i] + h * BufStrides[i]; - uint8_t* pDstSample2 = BufDatas2[i] + h * BufStrides[i]; - SplitBits(wEnd, pSample, pDstSample, pDstSample2); - } - } - } - - subpic.w = d->vi->width; - subpic.h = d->vi->height; - subpic.pitch = BufStrides[0]; - subpic.pitchUV = BufStrides[1]; - subpic.bits = BufDatas[0]; - subpic.bitsU = BufDatas[1]; - subpic.bitsV = BufDatas[2]; - subpic.bpp = 8; - subpic.type = MSP_YV12; + private: + VSFFrameBuf(const VSFFrameBuf*) = delete; + VSFFrameBuf* operator = (const VSFFrameBuf*) = delete; + }; + + template + class VSFYUVBuf : public VSFFrameBuf + { + uint8_t* Buffer; + + uint8_t* BufDatas[3]; + uint8_t* BufDatas2[3]; + int BufStrides[3]; + + const VSAPI* api; + const VSFilterData* d; + + template + static inline void SplitBits(int sampleCount, const uint16_t* src, uint8_t* dst1, uint8_t* dst2) + { + auto srcEnd = src + sampleCount; + auto sse2End = src + sampleCount - 8; + if (uintptr_t(src) & 0xf) + sse2End = src; + + __m128i lomask = _mm_set1_epi16(0xffi16); + while (src <= sse2End) + { + __m128i buf = _mm_load_si128((const __m128i*)src); + __m128i hi, lo; + + if (BC == 16) + { + hi = _mm_srli_epi16(buf, 8); + hi = _mm_packus_epi16(hi, hi); + lo = _mm_and_si128(buf, lomask); + lo = _mm_packus_epi16(lo, lo); + } + else if (BC == 10) + { + hi = _mm_srli_epi16(buf, 2); + hi = _mm_packus_epi16(hi, hi); + lo = _mm_slli_epi16(buf, 6); + lo = _mm_and_si128(lo, lomask); + lo = _mm_packus_epi16(lo, lo); + } + _mm_storel_epi64((__m128i*)dst1, hi); + _mm_storel_epi64((__m128i*)dst2, lo); + dst1 += 8; + dst2 += 8; + src += 8; + } + + while (src < srcEnd) + { + if (BITDEPTH == 10) + { + *dst1 = ((*src) >> 2) & 0xff; + *dst2 = ((*src) << 6) & 0xff; + } + else if (BITDEPTH == 16) + { + *dst1 = ((*src) >> 8) & 0xff; + *dst2 = (*src) & 0xff; + } + + dst1 += 1; + dst2 += 1; + src += 1; + } + } + + template + static inline void MergeBits(int sampleCount, uint16_t* dst, const uint8_t* src1, const uint8_t* src2) + { + auto srcEnd = dst + sampleCount; + auto sse2End = dst + sampleCount - 8; + if (uintptr_t(dst) & 0xf) + sse2End = dst; + + while (dst < sse2End) + { + __m128i hbuf = _mm_loadl_epi64((const __m128i*) src1); + __m128i lbuf = _mm_loadl_epi64((const __m128i*) src2); + hbuf = _mm_unpacklo_epi8(hbuf, _mm_setzero_si128()); + hbuf = _mm_slli_epi16(hbuf, 8); + lbuf = _mm_unpacklo_epi8(lbuf, _mm_setzero_si128()); + hbuf = _mm_adds_epi16(hbuf, lbuf); + if (BC == 10) + hbuf = _mm_srli_epi16(hbuf, 6); + + _mm_store_si128((__m128i*)dst, hbuf); + src1 += 8; + src2 += 8; + dst += 8; + } + + while (dst < srcEnd) + { + *dst = (*src1 << 8) | *src2; + if (BC == 10) + *dst >>= 6; + src1 += 1; + src2 += 1; + dst += 1; + } + } + + public: + ~VSFYUVBuf() + { + if (Buffer) + free(Buffer); + } + + VSFYUVBuf(const VSAPI* api, VSCore *core, const VSFilterData* d, const VSFrameRef* frame) + : api(api), d(d) + { + int totalSize = 0; + for (int i = 0; i < 3; ++i) + { + BufStrides[i] = api->getStride(frame, i); + totalSize += BufStrides[i] * d->vi->height / (i == 0 ? 1 : 2); + } + Buffer = (uint8_t*)malloc(totalSize); + + if (BITDEPTH <= 8) + { + BufDatas[0] = Buffer; + BufDatas[1] = BufDatas[0] + BufStrides[0] * d->vi->height; + BufDatas[2] = BufDatas[1] + BufStrides[1] * d->vi->height / 2; + + for (int i = 0; i < 3; ++i) + { + const uint8_t* p = api->getReadPtr(frame, i); + memcpy(BufDatas[i], p, api->getStride(frame, i) * d->vi->height / (i == 0 ? 1 : 2)); + } + } + else + { + for (int i = 0; i < 3; ++i) + BufStrides[i] /= 2; + + BufDatas[0] = Buffer; + BufDatas[1] = BufDatas[0] + BufStrides[0] * d->vi->height; + BufDatas[2] = BufDatas[1] + BufStrides[1] * d->vi->height / 2; + BufDatas2[0] = BufDatas[2] + BufStrides[2] * d->vi->height / 2; + BufDatas2[1] = BufDatas2[0] + BufStrides[0] * d->vi->height; + BufDatas2[2] = BufDatas2[1] + BufStrides[1] * d->vi->height / 2; + + for (int i = 0; i < 3; ++i) + { + const uint8_t* p = api->getReadPtr(frame, i); + int srcStride = api->getStride(frame, i); + + int wEnd = d->vi->width / (i == 0 ? 1 : 2); + int hEnd = d->vi->height / (i == 0 ? 1 : 2); + uint8_t* pDst = BufDatas[i]; + uint8_t* pDst2 = BufDatas2[i]; + + for (int h = 0; h < hEnd; ++h) + { + const uint16_t* pSample = reinterpret_cast(p + h * srcStride); + uint8_t* pDstSample = BufDatas[i] + h * BufStrides[i]; + uint8_t* pDstSample2 = BufDatas2[i] + h * BufStrides[i]; + SplitBits(wEnd, pSample, pDstSample, pDstSample2); + } + } + } + + subpic.w = d->vi->width; + subpic.h = d->vi->height; + subpic.pitch = BufStrides[0]; + subpic.pitchUV = BufStrides[1]; + subpic.bits = BufDatas[0]; + subpic.bitsU = BufDatas[1]; + subpic.bitsV = BufDatas[2]; + subpic.bpp = 8; + subpic.type = MSP_YV12; if (BITDEPTH > 8) { @@ -1095,124 +1095,124 @@ namespace VapourSynth { subpic2.bpp = 8; subpic2.type = MSP_YV12; } - } - - void WriteTo(VSFrameRef* frame) override - { - if (BITDEPTH <= 8) - { - for (int i = 0; i < 3; ++i) - { - int dstStride = api->getStride(frame, i); - uint8_t* pDst = api->getWritePtr(frame, i); - int srcStride = BufStrides[i]; - const uint8_t* pSrc = BufDatas[i]; - int wEnd = d->vi->width / (i == 0 ? 1 : 2); - int hEnd = d->vi->height / (i == 0 ? 1 : 2); - for (int h = 0; h < hEnd; ++h) - { - uint8_t* pDstRow = pDst + h * dstStride; - const uint8_t* pSrcRow = pSrc + h * srcStride; - memcpy(pDstRow, pSrcRow, wEnd); - } - } - } - else - { - for (int i = 0; i < 3; ++i) - { - int dstStride = api->getStride(frame, i); - uint8_t* pDst = api->getWritePtr(frame, i); - int srcStride = BufStrides[i]; - const uint8_t* pSrc = BufDatas[i]; - const uint8_t* pSrc2 = BufDatas2[i]; - int wEnd = d->vi->width / (i == 0 ? 1 : 2); - int hEnd = d->vi->height / (i == 0 ? 1 : 2); - for (int h = 0; h < hEnd; ++h) - { - uint16_t* pDstSample = reinterpret_cast(pDst + h * dstStride); - const uint8_t* pSrcSample = pSrc + h * srcStride; - const uint8_t* pSrcSample2 = pSrc2 + h * srcStride; - - MergeBits(wEnd, pDstSample, pSrcSample, pSrcSample2); - } - } - } - } - }; - - class VSFRGBBuf : public VSFFrameBuf - { + } + + void WriteTo(VSFrameRef* frame) override + { + if (BITDEPTH <= 8) + { + for (int i = 0; i < 3; ++i) + { + int dstStride = api->getStride(frame, i); + uint8_t* pDst = api->getWritePtr(frame, i); + int srcStride = BufStrides[i]; + const uint8_t* pSrc = BufDatas[i]; + int wEnd = d->vi->width / (i == 0 ? 1 : 2); + int hEnd = d->vi->height / (i == 0 ? 1 : 2); + for (int h = 0; h < hEnd; ++h) + { + uint8_t* pDstRow = pDst + h * dstStride; + const uint8_t* pSrcRow = pSrc + h * srcStride; + memcpy(pDstRow, pSrcRow, wEnd); + } + } + } + else + { + for (int i = 0; i < 3; ++i) + { + int dstStride = api->getStride(frame, i); + uint8_t* pDst = api->getWritePtr(frame, i); + int srcStride = BufStrides[i]; + const uint8_t* pSrc = BufDatas[i]; + const uint8_t* pSrc2 = BufDatas2[i]; + int wEnd = d->vi->width / (i == 0 ? 1 : 2); + int hEnd = d->vi->height / (i == 0 ? 1 : 2); + for (int h = 0; h < hEnd; ++h) + { + uint16_t* pDstSample = reinterpret_cast(pDst + h * dstStride); + const uint8_t* pSrcSample = pSrc + h * srcStride; + const uint8_t* pSrcSample2 = pSrc2 + h * srcStride; + + MergeBits(wEnd, pDstSample, pSrcSample, pSrcSample2); + } + } + } + } + }; + + class VSFRGBBuf : public VSFFrameBuf + { std::unique_ptr buffer; - const VSAPI* api; - const VSFilterData* d; + const VSAPI* api; + const VSFilterData* d; - public: - ~VSFRGBBuf() - { - } + public: + ~VSFRGBBuf() + { + } - VSFRGBBuf(const VSAPI* api, VSCore *core, const VSFilterData* d, const VSFrameRef* frame) - : api(api), d(d) - { + VSFRGBBuf(const VSAPI* api, VSCore *core, const VSFilterData* d, const VSFrameRef* frame) + : api(api), d(d) + { buffer = std::make_unique(d->vi->width * d->vi->height * 4); - const int srcStride = api->getStride(frame, 0); - const int tmpStride = d->vi->width*4; - const uint8_t * srcpR = api->getReadPtr(frame, 0); - const uint8_t * srcpG = api->getReadPtr(frame, 1); - const uint8_t * srcpB = api->getReadPtr(frame, 2); - uint8_t * VS_RESTRICT tmpp = buffer.get(); - - tmpp += tmpStride * (d->vi->height - 1); - - for (int y = 0; y < d->vi->height; y++) { - for (int x = 0; x < d->vi->width; x++) { - tmpp[x * 4] = srcpB[x]; - tmpp[x * 4 + 1] = srcpG[x]; - tmpp[x * 4 + 2] = srcpR[x]; - tmpp[x * 4 + 3] = 0ui8; - } - - srcpR += srcStride; - srcpG += srcStride; - srcpB += srcStride; - tmpp -= tmpStride; - } - - subpic.w = d->vi->width; - subpic.h = d->vi->height; - subpic.pitch = tmpStride; - subpic.bits = buffer.get(); - subpic.bpp = 32; - subpic.type = MSP_RGB32; - } - - void WriteTo(VSFrameRef* frame) override - { - const int tmpStride = d->vi->width * 4; - const int dstStride = api->getStride(frame, 0); - const uint8_t * tmpp = buffer.get(); - uint8_t * VS_RESTRICT dstpR = api->getWritePtr(frame, 0); - uint8_t * VS_RESTRICT dstpG = api->getWritePtr(frame, 1); - uint8_t * VS_RESTRICT dstpB = api->getWritePtr(frame, 2); - - tmpp += tmpStride * (d->vi->height - 1); - - for (int y = 0; y < d->vi->height; y++) { - for (int x = 0; x < d->vi->width; x++) { - dstpB[x] = tmpp[x * 4]; - dstpG[x] = tmpp[x * 4 + 1]; - dstpR[x] = tmpp[x * 4 + 2]; - } - - tmpp -= tmpStride; - dstpR += dstStride; - dstpG += dstStride; - dstpB += dstStride; - } - } - }; + const int srcStride = api->getStride(frame, 0); + const int tmpStride = d->vi->width*4; + const uint8_t * srcpR = api->getReadPtr(frame, 0); + const uint8_t * srcpG = api->getReadPtr(frame, 1); + const uint8_t * srcpB = api->getReadPtr(frame, 2); + uint8_t * VS_RESTRICT tmpp = buffer.get(); + + tmpp += tmpStride * (d->vi->height - 1); + + for (int y = 0; y < d->vi->height; y++) { + for (int x = 0; x < d->vi->width; x++) { + tmpp[x * 4] = srcpB[x]; + tmpp[x * 4 + 1] = srcpG[x]; + tmpp[x * 4 + 2] = srcpR[x]; + tmpp[x * 4 + 3] = 0ui8; + } + + srcpR += srcStride; + srcpG += srcStride; + srcpB += srcStride; + tmpp -= tmpStride; + } + + subpic.w = d->vi->width; + subpic.h = d->vi->height; + subpic.pitch = tmpStride; + subpic.bits = buffer.get(); + subpic.bpp = 32; + subpic.type = MSP_RGB32; + } + + void WriteTo(VSFrameRef* frame) override + { + const int tmpStride = d->vi->width * 4; + const int dstStride = api->getStride(frame, 0); + const uint8_t * tmpp = buffer.get(); + uint8_t * VS_RESTRICT dstpR = api->getWritePtr(frame, 0); + uint8_t * VS_RESTRICT dstpG = api->getWritePtr(frame, 1); + uint8_t * VS_RESTRICT dstpB = api->getWritePtr(frame, 2); + + tmpp += tmpStride * (d->vi->height - 1); + + for (int y = 0; y < d->vi->height; y++) { + for (int x = 0; x < d->vi->width; x++) { + dstpB[x] = tmpp[x * 4]; + dstpG[x] = tmpp[x * 4 + 1]; + dstpR[x] = tmpp[x * 4 + 2]; + } + + tmpp -= tmpStride; + dstpR += dstStride; + dstpG += dstStride; + dstpB += dstStride; + } + } + }; static const VSFrameRef *VS_CC vsfilterGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { const VSFilterData * d = static_cast(*instanceData); @@ -1223,29 +1223,29 @@ namespace VapourSynth { const VSFrameRef * src = vsapi->getFrameFilter(n, d->node, frameCtx); VSFrameRef * dst = vsapi->copyFrame(src, core); - std::unique_ptr frameBuf; - - if (d->vi->format->colorFamily == cmRGB) - { - frameBuf.reset(new VSFRGBBuf(vsapi, core, d, src)); - } - else if (d->vi->format->colorFamily == cmYUV) - { - if (d->vi->format->id == pfYUV420P8) - frameBuf.reset(new VSFYUVBuf<8>(vsapi, core, d, src)); - else if (d->vi->format->id == pfYUV420P10) - frameBuf.reset(new VSFYUVBuf<10>(vsapi, core, d, src)); - else if (d->vi->format->id == pfYUV420P16) - frameBuf.reset(new VSFYUVBuf<16>(vsapi, core, d, src)); - } - - if (frameBuf) - { - REFERENCE_TIME timestamp; - if (!d->vfr) - timestamp = static_cast(10000000i64 * n / d->fps); - else - timestamp = static_cast(10000000 * d->vfr->TimeStampFromFrameNumber(n)); + std::unique_ptr frameBuf; + + if (d->vi->format->colorFamily == cmRGB) + { + frameBuf.reset(new VSFRGBBuf(vsapi, core, d, src)); + } + else if (d->vi->format->colorFamily == cmYUV) + { + if (d->vi->format->id == pfYUV420P8) + frameBuf.reset(new VSFYUVBuf<8>(vsapi, core, d, src)); + else if (d->vi->format->id == pfYUV420P10) + frameBuf.reset(new VSFYUVBuf<10>(vsapi, core, d, src)); + else if (d->vi->format->id == pfYUV420P16) + frameBuf.reset(new VSFYUVBuf<16>(vsapi, core, d, src)); + } + + if (frameBuf) + { + REFERENCE_TIME timestamp; + if (!d->vfr) + timestamp = static_cast(10000000i64 * n / d->fps); + else + timestamp = static_cast(10000000 * d->vfr->TimeStampFromFrameNumber(n)); if (d->textsub) { @@ -1260,11 +1260,11 @@ namespace VapourSynth { d->vobsub->Render(frameBuf->subpic2, timestamp, d->fps); } - frameBuf->WriteTo(dst); - } + frameBuf->WriteTo(dst); + } - vsapi->freeFrame(src); - return dst; + vsapi->freeFrame(src); + return dst; } return nullptr; @@ -1287,13 +1287,13 @@ namespace VapourSynth { VSFilterData& d = *ud; int err; - const ::std::string filterName{ static_cast(userData) }; + const ::std::string filterName{ static_cast(userData) }; d.node = vsapi->propGetNode(in, "clip", 0, nullptr); d.vi = vsapi->getVideoInfo(d.node); if (!isConstantFormat(d.vi) || (d.vi->format->id != pfYUV420P8 && d.vi->format->id != pfRGB24 - && d.vi->format->id != pfYUV420P10 && d.vi->format->id != pfYUV420P16)) { + && d.vi->format->id != pfYUV420P10 && d.vi->format->id != pfYUV420P16)) { vsapi->setError(out, (filterName + ": only constant format YUV420P8, YUV420P10, YUV420P16 and RGB24 input supported").c_str()); vsapi->freeNode(d.node); return; From f0d0a98b6befc9d112ce8eec3a4d3d8cda6238f5 Mon Sep 17 00:00:00 2001 From: Masaiki Date: Mon, 4 May 2026 18:16:36 +0800 Subject: [PATCH 08/10] feat(csri): implement standard csri_stream_ext for subtitle streaming This commit implements the standard CSRI_EXT_STREAM_ASS extension in VSFilterMod, allowing callers (like xy-VSFilter) to feed subtitle packets dynamically instead of loading the entire file upfront. Key changes: - Included standard `csri/stream.h` and exposed the stream extension via `csri_query_ext`. - Implemented `vsfilter_init_stream`, `vsfilter_push_packet`, and `vsfilter_discard` to support standard streaming operations. - Added `m_sver` (script version) to `CSimpleTextSubtitle` and persisted it during header parsing (in `STS.cpp`). - Fixed fragile ASS/ASS2 format detection in `push_packet`: Instead of naively counting commas per packet (which broke on ASS tags like `\pos(x,y)`), it now reliably checks `m_sver >= 6` from the parsed header to determine if 9 or 10 fields are expected. - Replaced the non-existent `RemoveAllEntries()` with `RemoveAll()` and `CreateSegments()` in the stream discard logic to properly clear the render queue. --- src/subtitles/STS.cpp | 3 + src/subtitles/STS.h | 1 + src/vsfilter/csri/stream.h | 143 +++++++++++++++++++++++++++++++++++++ src/vsfilter/csriapi.cpp | 65 ++++++++++++++++- 4 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 src/vsfilter/csri/stream.h diff --git a/src/subtitles/STS.cpp b/src/subtitles/STS.cpp index 87cf420..b970336 100644 --- a/src/subtitles/STS.cpp +++ b/src/subtitles/STS.cpp @@ -1774,6 +1774,7 @@ static bool OpenSubStationAlpha(CTextFile* file, CSimpleTextSubtitle& ret, int C #endif } + if (fRet) ret.m_sver = sver; return(fRet); } @@ -2051,6 +2052,7 @@ CSimpleTextSubtitle::CSimpleTextSubtitle() m_collisions = 0; m_fScaledBAS = false; m_encoding = CTextFile::ASCII; + m_sver = 3; m_lcid = 0; m_ePARCompensationType = EPCTDisabled; m_dPARCompensation = 1.0; @@ -2185,6 +2187,7 @@ void CSimpleTextSubtitle::Empty() m_styles.Free(); m_segments.RemoveAll(); RemoveAll(); + m_sver = 3; #ifdef _VSMOD // indexing #ifdef INDEXING diff --git a/src/subtitles/STS.h b/src/subtitles/STS.h index 486141b..0092435 100644 --- a/src/subtitles/STS.h +++ b/src/subtitles/STS.h @@ -264,6 +264,7 @@ class CSimpleTextSubtitle : public CAtlArray LCID m_lcid; exttype m_exttype; tmode m_mode; + int m_sver; // script version: 3=unknown, 4=SSAv4, 5=ASS(v4.00+), 6=ASS2(v4.00++) CTextFile::enc m_encoding; CString m_path; diff --git a/src/vsfilter/csri/stream.h b/src/vsfilter/csri/stream.h new file mode 100644 index 0000000..cc24d97 --- /dev/null +++ b/src/vsfilter/csri/stream.h @@ -0,0 +1,143 @@ +/***************************************************************************** + * csri: common subtitle renderer interface + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + ****************************************************************************/ + +/** \file stream.h - subtitle streaming (MKV & co). + * $Id$ + * + * THE SPECIFICATION OF THIS EXTENSION IS TENTATIVE + * AND NOT FINALIZED YET + */ + +#ifndef _CSRI_STREAM_H +/** \cond */ +#define _CSRI_STREAM_H 20070119 +/** \endcond */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup stream csri.stream.* extensions. */ +/*@{*/ + +/** stream extension group. + * note: you cannot query for csri.stream, you need + * to query for one of the streaming formats instead, + * which will return a csri_stream_ext pointer. + */ +#define CSRI_EXT_STREAM (csri_ext_id)"csri.stream" + +/** Matroska-style ASS streaming. + * header contains standard SSA stuff, packet contains + * ReadOrder,Layer,Style,Speaker,MarginL,R,V,Effect,Text + */ +#define CSRI_EXT_STREAM_ASS CSRI_EXT_STREAM ".ass" +/** Simple text + timestamp streams */ +#define CSRI_EXT_STREAM_TEXT CSRI_EXT_STREAM ".text" +/* missing: USF, MPEG-4 TT */ + +/** stream extension information structure */ +struct csri_stream_ext { + /** create streaming renderer instance. + * \param renderer the renderer handle. + * \param header codec-private stream header. + * \param headerlen byte length of header data. + * \param flags openflags. + * + * not NULL if this extension is supported. + * may take various flags like csri_openerr_flag. + * + * the returned instance can be used with + * csri_request_fmt, csri_render and csri_close. + */ + csri_inst *(*init_stream)(csri_rend *renderer, + const void *header, size_t headerlen, + struct csri_openflag *flags); + + /** process a streamed packet. + * \param inst instance created with init_stream. + * \param packet stream packet data. + * \param packetlen byte length of packet. + * \param pts_start start timestamp from container. + * \param pts_end end timestamp from container. + * + * add a single packet to the renderer instance. + */ + void (*push_packet)(csri_inst *inst, + const void *packet, size_t packetlen, + double pts_start, double pts_end); + + /** discard processed packets. + * \param inst instance created with init_stream. + * \param all discard possibly-active packets too.\n + * a possibly-active packet is a packet which + * has not seen a csri_render call with a pts + * beyond its end timestamp yet. + * + * frees up memory, or can force a clean renderer. + * may be NULL if unsupported, check before calling! + */ + void (*discard)(csri_inst *inst, int all); +}; + +/** streaming openflag ext for controlling subtitle lifetime */ +#define CSRI_EXT_STREAM_DISCARD CSRI_EXT_STREAM ".discard" + +/** subtitle packet lifetime */ +enum csri_stream_discard { + /** lifetime: timestamp expiry. + * delete packets from csri_render if the current + * timestamp is beyond the packet's end timestamp. + * this should be the default + */ + CSRI_STREAM_DISCARD_TSEXPIRE = 0, + /** lifetime: discard immediately. + * discard all packets on returning from csri_render. + */ + CSRI_STREAM_DISCARD_IMMEDIATELY, + /** lifetime: discard explicitly. + * never discard packets, use csri_stream_ext.discard + */ + CSRI_STREAM_DISCARD_EXPLICIT +}; + +/** openflag for csri_stream_ext.init_stream */ +struct csri_stream_discard_flag { + /** the lifetime to be used for subtitle packets */ + enum csri_stream_discard lifetime; +}; + +/*@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _CSRI_STREAM_H */ diff --git a/src/vsfilter/csriapi.cpp b/src/vsfilter/csriapi.cpp index 93b9f68..dab024f 100644 --- a/src/vsfilter/csriapi.cpp +++ b/src/vsfilter/csriapi.cpp @@ -20,6 +20,7 @@ */ #include "stdafx.h" +#include "..\dsutil\text.h" #include #include #include "resource.h" @@ -43,6 +44,7 @@ extern "C" struct csri_vsfilter_inst }; typedef struct csri_vsfilter_inst csri_inst; #include "csri.h" +#include "csri/stream.h" #ifdef _VSMOD static csri_rend csri_vsfilter = "vsfiltermod"; #else @@ -188,9 +190,70 @@ CSRIAPI void csri_render(csri_inst *inst, struct csri_frame *frame, double time) } -// No extensions supported +// Stream extension implementation +static csri_inst *vsfilter_init_stream(csri_rend *renderer, const void *header, size_t headerlen, struct csri_openflag *flags) +{ + return csri_open_mem(renderer, header, headerlen, flags); +} + +static void vsfilter_push_packet(csri_inst *inst, const void *packet, size_t packetlen, double pts_start, double pts_end) +{ + if (!inst || !inst->rts || !packet || packetlen == 0) return; + + CStringA strA((LPCSTR)packet, packetlen); + CStringW str = UTF8To16(strA).Trim(); + + if (str.IsEmpty()) return; + + STSEntry stse; + int fields = (inst->rts->m_sver >= 6) ? 10 : 9; + + CAtlList sl; + Explode(str, sl, ',', fields); + if(sl.GetCount() == fields) + { + stse.readorder = wcstol(sl.RemoveHead(), NULL, 10); + stse.layer = wcstol(sl.RemoveHead(), NULL, 10); + stse.style = sl.RemoveHead(); + stse.actor = sl.RemoveHead(); + stse.marginRect.left = wcstol(sl.RemoveHead(), NULL, 10); + stse.marginRect.right = wcstol(sl.RemoveHead(), NULL, 10); + stse.marginRect.top = stse.marginRect.bottom = wcstol(sl.RemoveHead(), NULL, 10); + if(fields == 10) stse.marginRect.bottom = wcstol(sl.RemoveHead(), NULL, 10); + stse.effect = sl.RemoveHead(); + stse.str = sl.RemoveHead(); + } + + if(!stse.str.IsEmpty()) + { + inst->rts->Add(stse.str, true, (int)(pts_start * 1000), (int)(pts_end * 1000), + stse.style, stse.actor, stse.effect, stse.marginRect, stse.layer, stse.readorder); + } +} + +static void vsfilter_discard(csri_inst *inst, int all) +{ + if (inst && inst->rts) { + // VSFilter's RTS doesn't have an exact "discard" per-se for streaming, + // but we can remove all entries if `all` is requested (similar to NewSegment). + if (all) { + inst->rts->RemoveAll(); + inst->rts->CreateSegments(); + } + } +} + +static struct csri_stream_ext vsfilter_stream_ext = { + vsfilter_init_stream, + vsfilter_push_packet, + vsfilter_discard +}; + CSRIAPI void *csri_query_ext(csri_rend *rend, csri_ext_id extname) { + if (strcmp(extname, CSRI_EXT_STREAM_ASS) == 0) { + return &vsfilter_stream_ext; + } return 0; } From 08148e4216c1ed2e5d0fb3c94413101c7a4f978d Mon Sep 17 00:00:00 2001 From: Saiki Date: Tue, 5 May 2026 01:37:46 +0800 Subject: [PATCH 09/10] chore(ci): update CI configuration to use latest Windows runner and actions --- .github/workflows/c-cpp.yml | 16 +-- include/stdint.h | 247 ------------------------------------ 2 files changed, 8 insertions(+), 255 deletions(-) delete mode 100644 include/stdint.h diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index c106f15..8f15bb8 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -5,21 +5,21 @@ on: [push] jobs: build: - runs-on: windows-2019 + runs-on: windows-2022 steps: - - uses: actions/checkout@v2 - - name: build - shell: cmd - run: ("C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\msbuild" /t:Rebuild /p:WindowsTargetPlatformVersion=10.0.18362.0 /p:PlatformToolset=v142 /m /p:"Configuration=Release (MOD)" /p:Platform=Win32) + - uses: actions/checkout@v4 + - name: set msbuild (windows) + uses: ilammy/msvc-dev-cmd@v1 + - name: build-x86 + run: msbuild /t:Rebuild /p:WindowsTargetPlatformVersion=10.0 /p:PlatformToolset=v143 /m /p:"Configuration=Release (MOD)" /p:Platform=Win32 - name: build-x64 - shell: cmd - run: ("C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\msbuild" /t:Rebuild /p:WindowsTargetPlatformVersion=10.0.18362.0 /p:PlatformToolset=v142 /m /p:"Configuration=Release (MOD)" /p:Platform=x64) + run: msbuild /t:Rebuild /p:WindowsTargetPlatformVersion=10.0 /p:PlatformToolset=v143 /m /p:"Configuration=Release (MOD)" /p:Platform=x64 - name: copy shell: cmd run: cmake -E copy "bin\Win32\VSFilter\Release (MOD)\VSFilterMod.dll" dist\x86\VSFilterMod.dll && cmake -E copy "bin\x64\VSFilter\Release (MOD)\VSFilterMod.dll" dist\x64\VSFilterMod.dll - name: upload - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: VSFilterMod_bin path: dist diff --git a/include/stdint.h b/include/stdint.h deleted file mode 100644 index 2291c3b..0000000 --- a/include/stdint.h +++ /dev/null @@ -1,247 +0,0 @@ -// ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2008 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. The name of the author may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_STDINT_H_ // [ -#define _MSC_STDINT_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include - -// For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we should wrap include with 'extern "C++" {}' -// or compiler give many errors like this: -// error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#ifdef __cplusplus -extern "C" { -#endif -# include -#ifdef __cplusplus -} -#endif - -// Define _W64 macros to mark types changing their size, like intptr_t. -#ifndef _W64 -# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 -# define _W64 __w64 -# else -# define _W64 -# endif -#endif - - -// 7.18.1 Integer types - -// 7.18.1.1 Exact-width integer types - -// Visual Studio 6 and Embedded Visual C++ 4 doesn't -// realize that, e.g. char has the same size as __int8 -// so we give up on __intX for them. -#if (_MSC_VER < 1300) -typedef signed char int8_t; -typedef signed short int16_t; -typedef signed int int32_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -#else -typedef signed __int8 int8_t; -typedef signed __int16 int16_t; -typedef signed __int32 int32_t; -typedef unsigned __int8 uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -#endif -typedef signed __int64 int64_t; -typedef unsigned __int64 uint64_t; - - -// 7.18.1.2 Minimum-width integer types -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -typedef int64_t int_least64_t; -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -typedef uint64_t uint_least64_t; - -// 7.18.1.3 Fastest minimum-width integer types -typedef int8_t int_fast8_t; -typedef int16_t int_fast16_t; -typedef int32_t int_fast32_t; -typedef int64_t int_fast64_t; -typedef uint8_t uint_fast8_t; -typedef uint16_t uint_fast16_t; -typedef uint32_t uint_fast32_t; -typedef uint64_t uint_fast64_t; - -// 7.18.1.4 Integer types capable of holding object pointers -#ifdef _WIN64 // [ -typedef signed __int64 intptr_t; -typedef unsigned __int64 uintptr_t; -#else // _WIN64 ][ -typedef _W64 signed int intptr_t; -typedef _W64 unsigned int uintptr_t; -#endif // _WIN64 ] - -// 7.18.1.5 Greatest-width integer types -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; - - -// 7.18.2 Limits of specified-width integer types - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 - -// 7.18.2.1 Limits of exact-width integer types -#define INT8_MIN ((int8_t)_I8_MIN) -#define INT8_MAX _I8_MAX -#define INT16_MIN ((int16_t)_I16_MIN) -#define INT16_MAX _I16_MAX -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX _I64_MAX -#define UINT8_MAX _UI8_MAX -#define UINT16_MAX _UI16_MAX -#define UINT32_MAX _UI32_MAX -#define UINT64_MAX _UI64_MAX - -// 7.18.2.2 Limits of minimum-width integer types -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MIN INT64_MIN -#define INT_LEAST64_MAX INT64_MAX -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -// 7.18.2.3 Limits of fastest minimum-width integer types -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MIN INT64_MIN -#define INT_FAST64_MAX INT64_MAX -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -// 7.18.2.4 Limits of integer types capable of holding object pointers -#ifdef _WIN64 // [ -# define INTPTR_MIN INT64_MIN -# define INTPTR_MAX INT64_MAX -# define UINTPTR_MAX UINT64_MAX -#else // _WIN64 ][ -# define INTPTR_MIN INT32_MIN -# define INTPTR_MAX INT32_MAX -# define UINTPTR_MAX UINT32_MAX -#endif // _WIN64 ] - -// 7.18.2.5 Limits of greatest-width integer types -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -// 7.18.3 Limits of other integer types - -#ifdef _WIN64 // [ -# define PTRDIFF_MIN _I64_MIN -# define PTRDIFF_MAX _I64_MAX -#else // _WIN64 ][ -# define PTRDIFF_MIN _I32_MIN -# define PTRDIFF_MAX _I32_MAX -#endif // _WIN64 ] - -#define SIG_ATOMIC_MIN INT_MIN -#define SIG_ATOMIC_MAX INT_MAX - -#ifndef SIZE_MAX // [ -# ifdef _WIN64 // [ -# define SIZE_MAX _UI64_MAX -# else // _WIN64 ][ -# define SIZE_MAX _UI32_MAX -# endif // _WIN64 ] -#endif // SIZE_MAX ] - -// WCHAR_MIN and WCHAR_MAX are also defined in -#ifndef WCHAR_MIN // [ -# define WCHAR_MIN 0 -#endif // WCHAR_MIN ] -#ifndef WCHAR_MAX // [ -# define WCHAR_MAX _UI16_MAX -#endif // WCHAR_MAX ] - -#define WINT_MIN 0 -#define WINT_MAX _UI16_MAX - -#endif // __STDC_LIMIT_MACROS ] - - -// 7.18.4 Limits of other integer types - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -#define INTMAX_C INT64_C -#define UINTMAX_C UINT64_C - -#endif // __STDC_CONSTANT_MACROS ] - - -#endif // _MSC_STDINT_H_ ] From 3009b7459951dd6fa4ed4b61e2427263eeccd712 Mon Sep 17 00:00:00 2001 From: Masaiki Date: Sun, 10 May 2026 18:00:25 +0800 Subject: [PATCH 10/10] feat(csri): add support for BGRA pixel format in csri_request_fmt and csri_render --- src/vsfilter/csriapi.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vsfilter/csriapi.cpp b/src/vsfilter/csriapi.cpp index dab024f..077e084 100644 --- a/src/vsfilter/csriapi.cpp +++ b/src/vsfilter/csriapi.cpp @@ -125,6 +125,7 @@ CSRIAPI int csri_request_fmt(csri_inst *inst, const struct csri_fmt *fmt) // Check if pixel format is supported switch(fmt->pixfmt) { + case CSRI_F_BGRA: case CSRI_F_BGR_: case CSRI_F_BGR: case CSRI_F_YUY2: @@ -149,6 +150,13 @@ CSRIAPI void csri_render(csri_inst *inst, struct csri_frame *frame, double time) spd.h = inst->screen_res.cy; switch(inst->pixfmt) { + case CSRI_F_BGRA: + spd.type = MSP_RGBA; + spd.bpp = 32; + spd.bits = frame->planes[0]; + spd.pitch = frame->strides[0]; + break; + case CSRI_F_BGR_: spd.type = MSP_RGB32; spd.bpp = 32;