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;
}

EncodingGuide/Legacy/TASBlend last edited by adelikat on 8/7/2022 8:16 PM
Page History Latest diff List referrers View Source