View Single Post
Old 9th December 2010, 23:47   #81  |  Link
jmac698
Registered User
 
Join Date: Jan 2006
Posts: 1,867
New Filter: Glitch Analyzer (detect dropped frames)

Updated to 1.02 Dec 18/2010

Capture Glitch Analyzer Ver. 1.02 by jmac698 (glitchanalyzer.avs)
A video with pseudo-timecode is generated then analyzed to detect temporal capture problems such as duplicated, missing, or field-reversed frames

Why: Some capture cards drop or dupe frames with no other indication. This will interrupt pulldown sequences making IVTC impossible. It also affects various multicap techniques.

Update: I found the solution to the original problem; VirtualDub drops frames by default (and my last 5 years of captures are all ruined). Only the following should be checked:
correct timing
auto disable sync

Requirements: ColorMatrix (optional), only tested on AviSynth 2.58 (in some of my other scripts, 2.6 caused problems)

Code:
#Capture Glitch Analyzer Ver. 1.02 by jmac698 (glitchanalyzer.avs)
#A video with pseudo-timecode is generated then analyzed to detect temporal capture problems such as duplicated, missing, or field-reversed frames
#1.01: Bug Fixes; DarkBlue wouldn't join with Colourbars due to differing fps, undefined variable sep, more convenient video sources
#1.02: Proper fix for CreateSolidBackground fps (need 30000.0 to assume float)

#Why: Some capture cards drop or dupe frames with no other indication.  This will interrupt pulldown sequences making IVTC impossible.  It also affects various multicap techniques.

#Requirements: ColorMatrix (optional)

#Useful tools: AvsP, HCEncoder, VirtualDub

#Usage level: Diagnosis probably requires an intermediate level user familiar with encoding, capturing videos and script setup, and some data analysis skills.
#  However, fixing diagnosed capture problems automatically may require advanced scripting knowledge

#Use: 
#1. Modify this script to set mode to generate and type to "NTSC" or "PAL" then encode the resulting diagnostic video
#  If encoding to mpeg2 use interlaced, top field first (e.g. in HCencoder)
#2. Capture the diagnostic video (e.g. in VirtualDub)
#3. Set mode to analyze, set the captured video filename and the timecode log output filename
#4. Run the analysis by playing back this script once (i.e., load the modified glitchanalyzer.avs into MediaPlayer and play)
#Load the resulting timecode.txt into a spreadsheet and
#  note where the timecode skips or duplicates numbers (a perfect capture will count from 0 to 15 repeatedly).  It is up to you to interpret this.
#The darkblue intro will read as constant 0, the lightblue outtro will read ideally as constant 15, but in reality will may be random, 0's or 15's.  Adjust slicelevel in readtimecode or the value of lightblue or ignore.

#Problems:
#The timecode may not be decoded if the video is horizontally or vertically shifted beyond tolerance, and/or the video levels are beyond tolerance.
#It is up to you to shift, crop, and adjust levels as necessary.
#Glitches spanning more than about 4 frames should be double-checked with the labels contained on the video, because of ambiguity in the sequence (I haven't thought this out further).
#Bluescreens typically generated by a VCR due to loss of signal may lead to unreliable timecodes.  Adjust slicelevel in readtimecode so that a constant timecode is decoded.

#Results
#Some capture cards/software/driver combinations will drop frames
#  with no indication.  One example: duplicate a frame (freezeframe), next frame, skip frame, 
#  then back to proper frame.  This 'hiccup' repeats every 13-15 frames.  The dupe is numerically exact.
#This sequence would look like: 0,1 0,1 2,3 6,7... (when it should be: 0,1 2,3 4,5 6,7)
#The next step is to find a way to deal with the missing frames.
#Restoring missing frames could be done by capturing twice, and merging the two videos together (which is easier said than done).  Interpolation is another approach to restoring missing frames.
#It's also possible that you don't need the missing frames anyhow, because the video is anime or silent film which cotains many duplicates due to the low frame rate.

#Other uses: Changes in field parity can also be detected.  The colourbars can verify levels.  The 'darkblue' can help measure noise. The script is educational.  Other codes like Closed Captioning could be decoded.

#How it works: First some backgrounds are generated, frame/field count is added, then a call to AddTimecode inserts a test signal
#  into the last 6 lines of video.  Although only two lines are needed, another problem with
#  capture cards is de-centred capturing.  An analog signal has 486 lines, but capture cards
#  only record 480 of them.  They should be centred.  Mine is two lines too high, thus
#  cropping the first two lnes.  Therefore, 6 lines gives some leeway.
#The background 'darkblue' is meant to give time for "Play" (on your playback device) and other symbols to disappear
#  before the timecode part appears.  For VHS this also gives time for the transport/tape
#  to stabilize.  And it gives you time to start the capture! 
#The analyze function decodes two lines of pseudo-timecode into a 4 bit number,
#  which counts from 0 to 15 and repeats.  This is enough to detect several combinations
#  of duplicated, missing, parity reversed, or rearranged frames.  An absolute timecode would be more difficult to program.
#With glitches affecting more than about 4 frames, you should look at the actual video
#  to verify the absolute timecode, which is written in text.

#Functions:
#CreateColourbars(string type, string colourspace, float length) Creates colourbars with type as NTSC or PAL, colourspace as REC709 or REC601, length in seconds
#CreateSolidBackground(string type, string colourspace, float length, int c) Creates a solid background with type as NTSC or PAL, colourspace as REC709 or REC601, length in seconds, colour as YUV in c
#LabelVideoWithCounts(clip v) Labels a video with frame and field counts
#whiteline(clip template) Creates a white line using template for video properties
#blackline(clip template) Creates a black line using template for video properties
#createcode(clip template,int code) Creates a video representing a 4bit timecode using template for video properties
#createcodefields(clip template,int code) Creates a video representing a 4bit timecode using template for video properties.  The video has two time codes on two lines, for use in interlaced video.
#AddTimecode(v) Overlays a timecode onto the supplied video, using createcodefields for generation.
#readtimecode(clip videoin) Extracts the video lines representing a timecode and decodes it, labeling the results on the returned video and also writing the results to the timecode file
#ShiftVideo(clip v, int hshift, int vshift) Shifts a video in any direction, where hshift<0 means left and vshift<0 means up.  Use only multiples of 2.
#ShowTimeCode(clip v) Extracts the video lines representing a timecode.

videoindrive="C:\"#You may find this useful if you use external hard drives
videoindir="project001\multicap\"
videoinname="glitchdiagnostic.avi"
videoinfilename=videoindrive+videoindir+videoinname
#You may find it easier to just uncomment the line below:
#videoinfilename="C:\MyVideos\glitchdiagnostic.avi"
dataoutdrive="C:\"
dataoutdir="project001\multicap\"
dataoutname="timecode.txt"
dataoutfilename=dataoutdrive+dataoutdir+dataoutname
#videoinfilename="C:\project001\multicap\framedrops.txt"
#Various test videos
v1=AviSource("C:\project001\multicap\svideo-source-glitch.avi").trim(209+117,0)
v2=MPEG2Source("C:\project001\multicap\Pinnacle PCTV 800i.0.SVideoInput.d2v", cpu=0).trim(51,0)
vid=v1

generate=1#Used to set mode to create the diagnostic video
analyze=2#Analyze a supplied video
demo=3#Both generate and analyze a virtual video and generate a timecode file
mode=demo
type="NTSC"#Set to NTSC or PAL.  You definitely need to set this.
#Important: if you don't have the optional ColorMatrix plugin installed, please set REC601 below!
colourspace="REC709"#Choose REC709 when encoding to mpeg2, REC601 for divx.  It is only for creating correct colourbars.
DarkBlue=CreateSolidBackground(type,colourspace,15,$10B848)#Specify video system, colourspace, time in seconds, and colour in YUV (directly in the colourspace of your choosing, i.e., it's not converted).
LightBlue=CreateSolidBackground(type,colourspace,15,$7EB848)#Joke: Booktree 848 is a nice colour!
CreateColourbars(type,colourspace,60)
mode==analyze?vid:last
mode==analyze?nop:LabelVideoWithCounts#Label the last video with frame and field counts
mode==analyze?nop:AddTimecode#Add computer readable timecode to last video (this is not related to the timecode in professional VCRs)
mode==analyze?nop:darkblue.assumefps(last.framerate)+last+darkblue.assumefps(last.framerate)
sep=","
WriteFileStart(dataoutfilename, """ "Current_Frame"+sep+"Top_Timecode"+sep+"Bot_Timecode" """)
WriteFileEnd(dataoutfilename, """ "End of Timecode for the file "+videoinfilename """)
mode==generate?last:readtimecode#Decode timecode embedded in video and log timecodes
#ShiftVideo(0,0)#Used for development; can be used to align incoming diagnostic video
#ShowTimeCode#Show only the extracted timecode video lines for debugging

#--- Functions are defined below.  You may want to split this out to another file and Import. ---
function CreateColourbars(string type, string colourspace, float length) {
  fps=type=="NTSC"?30000/1001:250000/1001
  colorbars(width=720,pixel_type="YV12").trim(0,length*fps/2-1).ConvertAudioTo16bit#weave'd down to half the time
  colourspace=="REC709"?ColorMatrix(mode="Rec.601->Rec.709"):last#If you get an error message here, set colourspace to REC601
}

function CreateSolidBackground(string type, string colourspace, float length, int c) {
  fps=(type=="NTSC")?30000.0/1001:25000.0/1001
  blankclip(Round(5*fps),width=720,pixel_type="YV12",fps=fps,audio_rate=48000,stereo=true,color_yuv=c)
}

function LabelVideoWithCounts(clip v) {
  #Add framecount and field counts to a video
  v
  assumetff
  separatefields
  top=last.selecteven
  bot=last.selectodd
  #all this is to remove color shadows; where the text overlays, color is desaturated, due to YV12
  tu=top.utoy
  tv=top.vtoy
  bu=bot.utoy
  bv=bot.vtoy
  top1=top.ScriptClip("""subtitle("top field = " + string(current_frame*2),x=-1,y=100,size=20,text_color=$00FFFFFF,font_width=17)""")
  bot1=bot.ScriptClip("""subtitle("bot field = " + string(current_frame*2+1),x=-1,y=120,size=20,text_color=$00FFFFFF,font_width=17)""")
  top=ytouv(tu,tv,top1)
  bot=ytouv(bu,bv,bot1)
  interleave(top,bot)
  weave
  ScriptClip("""subtitle("frame = " + string(current_frame),x=-1,y=140,size=40,text_color=$00FFFFFF)""")
  assumefps("ntsc_video")
  assumetff
}

#creates a 2 pixel high white line, quarter width, with the same video properties as the template
function whiteline(clip template) {
  template.crop(0,0,template.width/4,-(template.height-2)).tweak(sat=0,bright=255)
}

function blackline(clip template) {
  template.crop(0,0,template.width/4,-(template.height-2)).tweak(sat=0,bright=-255)
}

function createcode(clip template,int code) {
  #make 4 bit code from 4 quarter width horizontal lines
  code=code % 16
  b3=code/8
  code=code-b3*8
  b2=code/4
  code=code-b2*4
  b1=code/2
  code=code-b1*2
  b0=code
  template
  stackhorizontal(b3==1?whiteline:blackline,b2==1?whiteline:blackline,b1==1?whiteline:blackline,b0==1?whiteline:blackline)
}

function createcodefields(clip template,int code) {
  code=code*2
  interleave(createcode(template,code),createcode(template,code+1))
  pointresize(template.width,4)
  assumefieldbased.assumetff.weave
  crop(0,0,0,-2)
}

function AddTimecode(v) {
  #adds a 4bit timecode on the last 6 lines of video
  #e.g. frame 0, top field reads 0000, bot field reads 0001
  ScriptClip(v,"""
    overlay(last,createcodefields(last,current_frame),y=last.height-6)
  """)
}

function readtimecode(clip videoin) {
  ScriptClip(videoin,"""
    t=last
    w=last.width
    q=w/4#this is used to divide a video line into 4 horizontal parts to locate bits in the timecode
    h=last.height#this is used to locate the timecode lines, and should compensate for NTSC or PAL
    slicelevel=126#this luma video level is used to distinguish between 1 and 0 bits in the timecode ('data slicer' is a common term in electronics for this purpose)
    sep=","#the separator character used for the output data
    pointresize(last.width,last.height*2)
    b3tv=crop(0,(h-4)*2,-3*q,-3*2)#the leftmost 1/4 of a horizontal video line contains the highest bit value of the timecode
    b2tv=crop(q,(h-4)*2,-2*q,-3*2)
    b1tv=crop(2*q,(h-4)*2,-q,-3*2)
    b0tv=crop(3*q,(h-4)*2,0,-3*2)
    b3bv=crop(0,(h-3)*2,-3*q,-2*2)
    b2bv=crop(q,(h-3)*2,-2*q,-2*2)
    b1bv=crop(2*q,(h-3)*2,-q,-2*2)
    b0bv=crop(3*q,(h-3)*2,0,-2*2) 
    b3t=b3tv.averageluma>=slicelevel?1:0#a 'white' line (i.e., luma>=slicelevel) represents a 1 bit
    b2t=b2tv.averageluma>=slicelevel?1:0
    b1t=b1tv.averageluma>=slicelevel?1:0
    b0t=b0tv.averageluma>=slicelevel?1:0
    b3b=b3bv.averageluma>=slicelevel?1:0
    b2b=b2bv.averageluma>=slicelevel?1:0
    b1b=b1bv.averageluma>=slicelevel?1:0
    b0b=b0bv.averageluma>=slicelevel?1:0
    tc=b3t*8+b2t*4+b1t*2+b0t#top timecode
    bc=b3b*8+b2b*4+b1b*2+b0b#bot timecode
    t
    subtitle("decoded top field timecode="+string(tc))
    subtitle("decoded bot field timecode="+string(bc),y=16)
    WriteFile(dataoutfilename, "string(current_frame)+sep+string(tc)+sep+string(bc)")
  """)#tip: this could be potentially modified into a closed caption decoder as well
}

function ShiftVideo(clip v, int hshift, int vshift) {#Shift a video in any direction, hshift>0 shifts right, vshift>0 shifts down, use multiples of 2
  v
  l=hshift<0?-hshift:0
  r=hshift<0?0:hshift
  up=vshift<0?-vshift:0
  dn=vshift<0?0:vshift
  crop(l,up,0,0).addborders(0,0,l,up)
  crop(0,0,-r,-dn).addborders(r,dn,0,0)
}

function ShowTimeCode(clip v) {#Show only the extracted timecode video lines for debugging
  v
  w=last.width
  q=w/4#this is used to divide a video line into 4 horizontal parts to locate bits in the timecode
  h=last.height#this is used to locate the timecode lines, and should compensate for NTSC or PAL
  crop(0,h-4,0,2)
}

Last edited by jmac698; 18th December 2010 at 22:47. Reason: Warning about AviSynth Compatibility
jmac698 is offline   Reply With Quote