Some streaming sites, in particular YouTube, don't support frame rates much larger than 30 fps. As such, they do a poor job of displaying 30Hz flicker effects present in games such as Super Metroid and the Mega Man X series.
To combat this, creaothceann proposed some frame blending techniques. The best-received of these has been one which combines pairs of frames in a 33%/67% ratio, with which frame in a pair being more visible alternating for every pair (thus, 33%/67% for one pair, 67%/33% for the following pair, and so on). This technique has come to be known as TASBlend.
Some implementations of TASBlend follow.
function TASBlend(clip c, float "ratio") {
# reduces framerate to 1/2 but leaves flicker effects partly visible
# blends frame pairs with alternating opacity (default is 2/3+1/3;1/3+2/3)
# optional "ratio" is the opacity of the first frame out of the four
ratio = default(ratio, 2.0 / 3)
opacity1 = round((1 - ratio) * 257)
opacity2 = round(( ratio) * 257)
c
Interleave(Layer(SelectEvery(4, 0), SelectEvery(4, 1), level=opacity1),
\ Layer(SelectEvery(4, 2), SelectEvery(4, 3), level=opacity2))
}
If only certain segments of the clip are to be blended, creaothceann suggests using this in the following fashion:
function Replace(clip c, int i, int j, clip d) {
Assert(i >= 0, "Replace: parameter i is negative")
Assert(j >= 0, "Replace: parameter j is negative")
p1 = c.Trim(0 , -i)
p2 = d.Trim(i , j)
p3 = c.Trim(j + 1, 0)
p1 = (i == 0) ? c.Trim(0, -1).DeleteFrame(0) : p1
p3 = (j == 0) ? c.Trim(0, -1).DeleteFrame(0) : p3
p1 + p2 + p3
return (c.HasAudio) ? last.AudioDub(c) : last
}
AVISource("video.avi")
blended = TASBlend
ChangeFPS(FrameRate / 2)
Replace( 500, 999, blended)
Replace( 2000, 2999, blended)
Replace(70000, 71999, blended)
...
sgrunt has written the following filter in the spirit of his duplicate frame filter, which accepts raw RGB24 frames on
stdin
and outputs blended/unblended frames at half the framerate on stdout
.
Usage is:
tasblend <width> <height> <startframe> <endframe> [<startframe> <endframe> [...] ]
where
-
<width>
and<height>
are the height of a frame; and - each pair of
<startframe>
and<endframe>
signifies frame ranges from the input to be blended - to blend an entire clip, set<startframe>
to 0 and<endframe>
to a value larger than the number of frames in the clip.
This has not been tested thoroughly, so use at your own risk (and report bugs to sgrunt).
A version that has gamma correction is available at http://engelsish.org/tasblend-lookup.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
unsigned char *firstframe, *secondframe, *outframe;
unsigned int width = 0, height = 0, framesize = 0, curarg = 3;
unsigned long startframe = 0, endframe = 0, curframe = 0, i = 0;
unsigned short first33 = 1;
FILE* outfile = 0;
if (argc < 5)
{
fprintf(stderr, "usage: tasblend <width> <height> <startframe> <endframe> [<startframe> <endframe> [...]]\n");
exit(1);
}
width = atoi(argv[1]);
height = atoi(argv[2]);
startframe = atoi(argv[3]);
endframe = atoi(argv[4]);
framesize = width*height*3;
firstframe = (unsigned char *)malloc(sizeof(unsigned char)*framesize);
if (!firstframe)
{
perror("Couldn't malloc firstframe");
exit(2);
}
secondframe = (unsigned char *)malloc(sizeof(unsigned char)*framesize);
if (!secondframe)
{
perror("Couldn't malloc secondframe");
free(firstframe);
exit(3);
}
outframe = (unsigned char *)malloc(sizeof(unsigned char)*framesize);
if (!outframe)
{
perror("Couldn't malloc outframe");
free(firstframe);
free(secondframe);
exit(4);
}
while (!feof(stdin))
{
if (fread(firstframe, sizeof(unsigned char), framesize, stdin) < framesize)
break;
if (fread(secondframe, sizeof(unsigned char), framesize, stdin) < framesize)
{
fwrite(firstframe, sizeof(unsigned char), framesize, stdout);
break;
}
if (curframe >= startframe && curframe < endframe)
{
if (first33 > 0)
{
for (i = 0; i < framesize; i++)
outframe[i] = (firstframe[i] * 33 + secondframe[i] * 67) / 100;
}
else
{
for (i = 0; i < framesize; i++)
outframe[i] = (firstframe[i] * 67 + secondframe[i] * 33) / 100;
}
fwrite(outframe, sizeof(unsigned char), framesize, stdout);
first33 = (first33 > 0) ? 0 : 1;
}
else
{
if (curframe >= endframe && curarg+1 < argc)
{
curarg += 2;
if (curarg+1 < argc)
{
first33 = 1;
startframe = atoi(argv[curarg]);
endframe = atoi(argv[curarg+1]);
}
}
fwrite(firstframe, sizeof(unsigned char), framesize, stdout);
}
curframe += 2;
}
free(firstframe);
free(secondframe);
free(outframe);
return 0;
}