Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
## Change log

### 0.36.0-dev.4 (20251205)
### 0.36.0-dev.x (20251209)
* Fix hinting
* Fix ass_set_storage_size not be called
* Switch submodule build system to meson
* Update avisynth(plus) headers to v12
* Update libass to 0.17.4
* Unicode-safe file reading
* Add frame size parameters
* Add set_default_storage_size boolean
* Use frame properties if they available for "YCbCr Matrix: None"
* Work with frame copy instead of just modifying it

### 0.35 (20210304)
* Windows MSVC: Update to libass v0.15
Expand All @@ -21,7 +24,7 @@
* Fix: possible crash on initializing phase (buffer overread, linux crashed, Windows was just lucky)

### 0.34 (20210301)
* Fix the fix: revert matrix change made in 0.33
* Fix the fix: revert matrix change made in 0.33
* Fix: Check matrix from .ASS file "YCbCr Matrix:" section besides "Video Colorspace:"
Recognized values are "tv.601" and "tv.709"

Expand Down
20 changes: 6 additions & 14 deletions src/assrender.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,6 @@ static const char* detect_bom(const char* buf, const size_t bufsize) {
return "UTF-8";
}

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <sys/stat.h>
#endif

#ifdef _WIN32
static wchar_t *utf8_to_utf16le(const char *data) {
const int out_size = MultiByteToWideChar(CP_UTF8, 0, data, -1, NULL, 0);
Expand Down Expand Up @@ -216,7 +209,6 @@ AVS_Value AVSC_CC assrender_create(AVS_ScriptEnvironment* env, AVS_Value args,
AVS_FilterInfo* fi;
AVS_Clip* c = avs_new_c_filter(env, &fi, avs_array_elt(args, 0), 1);
const AVS_VideoInfo *vi = &fi->vi;
char e[250];

const char* f = avs_as_string(avs_array_elt(args, 1));
const char* vfr = avs_as_string(avs_array_elt(args, 2));
Expand Down Expand Up @@ -318,7 +310,7 @@ AVS_Value AVSC_CC assrender_create(AVS_ScriptEnvironment* env, AVS_Value args,
if (!strcasecmp(strrchr(f, '.'), ".srt")) {
FILE* fp = open_utf8_filename(f, "r");
if (!fp) {
sprintf(e, "AssRender: input file '%s' does not exist or is not a regular file", f);
const char* e = avs_sprintf(env, "AssRender: input file '%s' does not exist or is not a regular file", f);
v = avs_new_value_error(e);
avs_release_clip(c);
return v;
Expand All @@ -330,15 +322,15 @@ AVS_Value AVSC_CC assrender_create(AVS_ScriptEnvironment* env, AVS_Value args,

fp = open_utf8_filename(f, "rb");
if (!fp) {
sprintf(e, "AssRender: input file '%s' does not exist or is not a regular file", f);
const char* e = avs_sprintf(env, "AssRender: input file '%s' does not exist or is not a regular file", f);
v = avs_new_value_error(e);
avs_release_clip(c);
return v;
}

buf = read_file_bytes(fp, &bufsize);
if (!buf) {
sprintf(e, "AssRender: unable to read '%s'", f);
const char* e = avs_sprintf(env, "AssRender: unable to read '%s'", f);
v = avs_new_value_error(e);
avs_release_clip(c);
return v;
Expand All @@ -358,7 +350,7 @@ AVS_Value AVSC_CC assrender_create(AVS_ScriptEnvironment* env, AVS_Value args,
}

if (!ass) {
sprintf(e, "AssRender: unable to parse '%s'", f);
const char* e = avs_sprintf(env, "AssRender: unable to parse '%s'", f);
v = avs_new_value_error(e);
avs_release_clip(c);
return v;
Expand All @@ -371,7 +363,7 @@ AVS_Value AVSC_CC assrender_create(AVS_ScriptEnvironment* env, AVS_Value args,
FILE* fh = open_utf8_filename(vfr, "r");

if (!fh) {
sprintf(e, "AssRender: could not read timecodes file '%s'", vfr);
const char* e = avs_sprintf(env, "AssRender: could not read timecodes file '%s'", vfr);
v = avs_new_value_error(e);
avs_release_clip(c);
return v;
Expand All @@ -380,7 +372,7 @@ AVS_Value AVSC_CC assrender_create(AVS_ScriptEnvironment* env, AVS_Value args,
data->isvfr = 1;

if (fscanf(fh, "# timecode format v%d", &ver) != 1) {
sprintf(e, "AssRender: invalid timecodes file '%s'", vfr);
const char* e = avs_sprintf(env, "AssRender: invalid timecodes file '%s'", vfr);
v = avs_new_value_error(e);
avs_release_clip(c);
return v;
Expand Down
54 changes: 31 additions & 23 deletions src/assrender.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,50 @@
#include <string.h>
#include <time.h>
#include <ass/ass.h>

#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#else
# include <sys/stat.h>
#endif

#include "avs/config.h"
#ifdef AVS_WINDOWS
#include "avisynth_c.h"
# include "avisynth_c.h"
#else
#include <avisynth/avisynth_c.h>
# include <avisynth/avisynth_c.h>
#endif

#if defined(_MSC_VER)
#define __NO_ISOCEXT
#define __NO_INLINE__
# define __NO_ISOCEXT
# define __NO_INLINE__

#define strcasecmp _stricmp
#define atoll _atoi64
# define strcasecmp _stricmp
# define atoll _atoi64
#endif

typedef struct {
// premultiplied coefficients for integer scaled arithmetics
int y_r, y_g, y_b;
int u_r, u_g, u_b;
int v_r, v_g, v_b;
int offset_y;
bool valid;
// premultiplied coefficients for integer scaled arithmetics
int y_r, y_g, y_b;
int u_r, u_g, u_b;
int v_r, v_g, v_b;
int offset_y;
bool valid;
} ConversionMatrix;

typedef enum {
MATRIX_NONE = 0,
MATRIX_BT601,
MATRIX_PC601,
MATRIX_BT709,
MATRIX_PC709,
MATRIX_PC2020,
MATRIX_BT2020,
MATRIX_TVFCC,
MATRIX_PCFCC,
MATRIX_TV240M,
MATRIX_PC240M
MATRIX_NONE = 0,
MATRIX_BT601,
MATRIX_PC601,
MATRIX_BT709,
MATRIX_PC709,
MATRIX_PC2020,
MATRIX_BT2020,
MATRIX_TVFCC,
MATRIX_PCFCC,
MATRIX_TV240M,
MATRIX_PC240M
} matrix_type;

typedef void (* fPixel)(uint8_t** sub_img, uint8_t** data, uint32_t* pitch, uint32_t width, uint32_t height);
Expand Down
10 changes: 6 additions & 4 deletions src/include/avs/capi.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@
#endif
#endif

#ifdef __cplusplus
# define EXTERN_C extern "C"
#else
# define EXTERN_C
#ifndef EXTERN_C
# ifdef __cplusplus
# define EXTERN_C extern "C"
# else
# define EXTERN_C
# endif
#endif

#ifdef AVS_WINDOWS
Expand Down
118 changes: 91 additions & 27 deletions src/render.c
Original file line number Diff line number Diff line change
Expand Up @@ -893,58 +893,122 @@ AVS_VideoFrame* AVSC_CC assrender_get_frame(AVS_FilterInfo* p, int n)
udata* ud = (udata*)p->user_data;
ASS_Image* img;
AVS_VideoFrame* src;

AVS_VideoFrame* dst;
int64_t ts;
int changed;

// 1) Get upstream frame
src = avs_get_frame(p->child, n);

avs_make_writable(p->env, &src);
// 2) Allocate a new writable destination frame
dst = avs_new_video_frame_a(p->env, &p->vi, AVS_FRAME_ALIGN);
if (!dst) {
avs_release_video_frame(src);
return 0;
}

// 3) Copy src -> dst (real pixel copy), then free src

if (avs_is_planar(&p->vi)) {
if (avs_is_rgb(&p->vi)) {
// Planar RGB: R, G, B planes
const int planes[3] = { AVS_PLANAR_R, AVS_PLANAR_G, AVS_PLANAR_B };
for (int i = 0; i < 3; ++i) {
const uint8_t* srcp = avs_get_read_ptr_p(src, planes[i]);
int src_pitch = avs_get_pitch_p(src, planes[i]);
uint8_t* dstp = avs_get_write_ptr_p(dst, planes[i]);
int dst_pitch = avs_get_pitch_p(dst, planes[i]);
int row_size = avs_get_row_size_p(src, planes[i]);
int height = avs_get_height_p(src, planes[i]);
avs_bit_blt(p->env, dstp, dst_pitch, srcp, src_pitch, row_size, height);
}
}
else {
// Planar YUV: Y, U, V planes
const int planes[3] = { AVS_PLANAR_Y, AVS_PLANAR_U, AVS_PLANAR_V };
for (int i = 0; i < 3; ++i) {
const uint8_t* srcp = avs_get_read_ptr_p(src, planes[i]);
int src_pitch = avs_get_pitch_p(src, planes[i]);
uint8_t* dstp = avs_get_write_ptr_p(dst, planes[i]);
int dst_pitch = avs_get_pitch_p(dst, planes[i]);
int row_size = avs_get_row_size_p(src, planes[i]);
int height = avs_get_height_p(src, planes[i]);
avs_bit_blt(p->env, dstp, dst_pitch, srcp, src_pitch, row_size, height);
}
}
}
else {
// Packed formats (e.g. RGB32, YUY2, etc.)
const uint8_t* srcp = avs_get_read_ptr_p(src, AVS_DEFAULT_PLANE);
int src_pitch = avs_get_pitch_p(src, AVS_DEFAULT_PLANE);
uint8_t* dstp = avs_get_write_ptr_p(dst, AVS_DEFAULT_PLANE);
int dst_pitch = avs_get_pitch_p(dst, AVS_DEFAULT_PLANE);
int row_size = avs_get_row_size_p(src, AVS_DEFAULT_PLANE);
int height = avs_get_height_p(src, AVS_DEFAULT_PLANE);
avs_bit_blt(p->env, dstp, dst_pitch, srcp, src_pitch, row_size, height);
}

// Original frame no longer needed locally
avs_release_video_frame(src);

// 4) Compute timestamp for libass
if (!ud->isvfr) {
// its a casting party!
// it's a casting party!
ts = (int64_t)n * (int64_t)1000 * (int64_t)p->vi.fps_denominator / (int64_t)p->vi.fps_numerator;
} else {
}
else {
ts = ud->timestamp[n];
}

// 5) Render ASS
img = ass_render_frame(ud->ass_renderer, ud->ass, ts, &changed);

if (img) {
uint32_t height, width, pitch[2];
uint32_t width = p->vi.width;
uint32_t height = p->vi.height;
uint8_t* data[3];
uint32_t pitch[2];

// 6) Build write pointers for the copied frame (dst)
if (avs_is_planar(&p->vi) && !ud->greyscale) {
if (avs_is_rgb(&p->vi)) {
// planar RGB as 444
data[0] = avs_get_write_ptr_p(src, AVS_PLANAR_R);
data[1] = avs_get_write_ptr_p(src, AVS_PLANAR_G);
data[2] = avs_get_write_ptr_p(src, AVS_PLANAR_B);
pitch[0] = avs_get_pitch_p(src, AVS_DEFAULT_PLANE);
}
else {
data[0] = avs_get_write_ptr_p(src, AVS_PLANAR_Y);
data[1] = avs_get_write_ptr_p(src, AVS_PLANAR_U);
data[2] = avs_get_write_ptr_p(src, AVS_PLANAR_V);
pitch[0] = avs_get_pitch_p(src, AVS_PLANAR_Y);
pitch[1] = avs_get_pitch_p(src, AVS_PLANAR_U);
}
if (avs_is_rgb(&p->vi)) {
// planar RGB as 4:4:4
data[0] = avs_get_write_ptr_p(dst, AVS_PLANAR_R);
data[1] = avs_get_write_ptr_p(dst, AVS_PLANAR_G);
data[2] = avs_get_write_ptr_p(dst, AVS_PLANAR_B);
pitch[0] = avs_get_pitch_p(dst, AVS_DEFAULT_PLANE);
}
else {
data[0] = avs_get_write_ptr_p(dst, AVS_PLANAR_Y);
data[1] = avs_get_write_ptr_p(dst, AVS_PLANAR_U);
data[2] = avs_get_write_ptr_p(dst, AVS_PLANAR_V);
pitch[0] = avs_get_pitch_p(dst, AVS_PLANAR_Y);
pitch[1] = avs_get_pitch_p(dst, AVS_PLANAR_U);
}
}
else {
data[0] = avs_get_write_ptr_p(src, AVS_DEFAULT_PLANE);
pitch[0] = avs_get_pitch_p(src, AVS_DEFAULT_PLANE);
data[0] = avs_get_write_ptr_p(dst, AVS_DEFAULT_PLANE);
pitch[0] = avs_get_pitch_p(dst, AVS_DEFAULT_PLANE);
}

height = p->vi.height;
width = p->vi.width;

// 7) Rebuild cached subtitle bitmap if changed
if (changed) {
memset(ud->sub_img[0], 0x00, height * width * ud->pixelsize);
ud->f_make_sub_img(img, ud->sub_img, width, ud->bits_per_pixel, ud->rgb_fullscale, &ud->mx);
memset(ud->sub_img[0], 0, height * width * ud->pixelsize);
ud->f_make_sub_img(
img,
ud->sub_img,
width,
ud->bits_per_pixel,
ud->rgb_fullscale,
&ud->mx
);
}

// 8) Blend ASS into the copied frame
ud->apply(ud->sub_img, data, pitch, width, height);
}

return src;
// 9) Return the modified copy
return dst;
}