View Page Source

Back to Page
Revision 13 (current)
Edited by adelikat on 8/7/2022 8:16 PM
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 [Forum/Posts/279714|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.

%%TAB AVISynth
%%SRC_EMBED avs
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)) 
}
%%END_EMBED
If only certain segments of the clip are to be blended, creaothceann [Forum/Posts/280007|suggests] using this in the following fashion:
%%SRC_EMBED avs
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) 
... 
%%END_EMBED

%%TAB Command-line filter (C)
[user:sgrunt] has written the following filter in the spirit of [EncodingGuide/Legacy/DedupC|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 [user:sgrunt]).

A version that has gamma correction is available at http://engelsish.org/tasblend-lookup.c

%%SRC_EMBED 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;
}
%%END_EMBED
%%TAB_END