View Full Version : Cue Points / Frames
tacman1123
29th May 2008, 12:52
Is there a way to add a Cue Point to an Avisynth output? Basically I'm thinking of something like
a1=AviSource()
a2=AviSource().AddCue('Starting a2')
a1+a2
And that a cue file would be creating that fired at event when a2 started.
Alternatively, I could create my own, if I had a way to tell what frame I was on when a2 started, but that doesn't seem possible with the Avisynth scripting language (I'm thinking of an event that would be fired upon rendering, that's much more OOP-like). The end result could even just be a file with two columns, FrameNumber and event string (or timecode and event string).
I don't think this is possible, but I'm continually amazed at what Avisynth can do, so I figured I'd ask.
Thx,
Tac
gzarkadas
29th May 2008, 14:48
Maybe this will do the job (beware, I havent' actually tested it :)):
cue_text = "Starting a2"
a1=AviSource()
a2=AviSource()
a2 = ScriptClip(a2, """
return current_frame > 0 \
? last \
: last.WriteFile (http://avisynth.org/mediawiki/WriteFile)("cue_file.txt", "cue_text", flush=true)
""")
a1+a2
tacman1123
29th May 2008, 18:23
Thanks! I had just found the Runtime Environment, but wasn't quite sure how to use to to achieve the cue points.
Can this be written as a function, e.g.
a2.add_cue("a2")
function add_cue(clip c, string cue_text) {
ScriptClip(c, """
return current_frame > 0 \
? last \
: last.WriteFile("cue_file.txt", cue_text, flush=true)
""")
}
Or would c.current_frame be better?
Hmm, may need some more time to think about this one, there's some interesting possibilities here. Thanks for getting me started.
Tac
gzarkadas
29th May 2008, 19:46
...Can this be written as a function, e.g....
The problem is that cue_text must be a variable in script scope, not a function argument. In addition, if you observe carefully my example, you'll see that I have enclosed cue_text in quotes at the WriteFile call; you must pass the name of the variable in WriteFile, not the content of the variable.
Thus your example must be written and be used as follows:
function add_cue(clip c, string script_var_name) {
ScriptClip(c, """
return current_frame > 0 \
? last \
: last.WriteFile("cue_file.txt", script_var_name, flush=true)
""")
}
cue2 = "a2"
cue3 = "a3"
cue4 = "a4"
a1 = AviSource()
a2 = AviSource().add_cue("cue2")
a3 = AviSource().add_cue("cue3")
a4 = AviSource().add_cue("cue4")
a1 + a2 + a3 + a4
Tip: If you want to append all strings in the file instead of re-writing its contents at each call, use this version of add_cue:
function add_cue(clip c, string script_var_name, bool "clear") {
keep = Default(clear, true)
ScriptClip(c, """
return current_frame > 0 \
? last \
: last.WriteFile("cue_file.txt", script_var_name, append=keep, flush=true)
""")
}
Now you only have to supply a true value for clear when you want to erase cue_file.txt before writing; else you don;t have to supply it at all.
Gavino
29th May 2008, 20:47
Since WriteFile already operates within the runtime environment, and has a convenient conditional variant, I think you can do away with ScriptClip and just write
function add_cue(clip c, string filename, string script_var_name, bool "keep") {
keep = Default(keep, true)
WriteFileIf(c, filename, "current_frame==0", script_var_name, append=keep, flush=true)
}
(I've also made the filename into a parameter)
Gavino
29th May 2008, 22:47
A further refinement - I think tac wanted the overall frame number to appear in the cue file too.
That's a bit trickier because the only place that info is known is at the end of the filter chain and we want it to be available to add_cue. However, the magic of AviSynth's run-time features comes to the rescue again. Extend add_cue to be:
function add_cue(clip c, string filename, string script_var_name, bool "keep") {
keep = Default(keep, true)
WriteFileIf(c, filename, "current_frame==0", "cueFrame", script_var_name, append=keep, flush=true)
}
and add the following line to the end of your script:
FrameEvaluate("cueFrame=current_frame")
and there you are. Example use:
file="cue.txt" # might need to be full path name?
text=": starting a2"
a1=BlankClip(100).Subtitle("A1")
a2=BlankClip(100).Subtitle("A2").add_cue(file, "text")
a1+a2
FrameEvaluate("cueFrame=current_frame")
should produce "100: starting a2" in the output file when the script is rendered.
For those new to this runtime stuff, it's important to realise that the variable cueFrame is not just set once at the end (where it would be equivalent to Framecount()), but is continuously updated for each frame of the complete video. Thus the relevant value is available to WriteFileIf (inside add_cue), to be written when the appropriate condition is triggered, (ie when rendering the first frame of the clip to which add_cue is applied).
Gavino
31st May 2008, 12:52
To follow up my own post yet again (sorry), this version removes the nuisance that the cue text had to be passed indirectly as the name of a variable, as decribed by gzarkadas in post #4.
function add_cue(clip c, string filename, string cue, bool "keep") {
keep = Default(keep, true)
cue = chr(34)+cue+chr(34)
WriteFileIf(c, filename, "current_frame==0", "cueFrame", cue, append=keep, flush=true)
}
So you can now pass the text directly as a literal, eg add_cue(..., " starting a2"). Of course you can still pass a variable if you want (but in the normal way, without having to enclose the name in quotes).
mikeytown2
31st May 2008, 17:03
I opened up notepad and typed in ALT-034. nice its a ". would tripple quotes """ work instead of char(34)?
Gavino
31st May 2008, 19:39
would tripple quotes """ work instead of char(34)?
If you're asking could the line
cue = chr(34)+cue+chr(34)
be replaced by
cue = """cue"""
then the answer is no - triple quotes would just be as wrong as single quotes here - the quotes need to become part of the string's value, rather than simply delimiting it.
However, you've made me realise that it would be safer as
cue = chr(34)+chr(34)+chr(34)+cue+chr(34)+chr(34)+chr(34) # triple-quote the string
which would allow the original cue string (passed in as the parameter) to contain internal quotes.
tacman1123
7th July 2008, 19:30
I'm now trying to do the reverse of what I was going to do with this thread. Basically I'm implementing a closed caption system, and was planning to use ConditionalReader to do so, but it appears that the only types for ConditionalReader are book, int and float. I'm surprised, I would have thought strings would be easy to implement, e.g.
file.txt
Type string
Default "" (or just nothing)
R 10 180 First Caption
R 185 250 Second Caption
Then simply
ScriptClip("subtitle(myvar)")
ConditionalReader("file.txt", "myvar", false)
Suggestions on how to proceed?
Thx,
Tac
Gavino
7th July 2008, 20:10
the only types for ConditionalReader are bool, int and float. I'm surprised, I would have thought strings would be easy to implement
I too was surprised to discover this recently. However, it's a bit more difficult for strings because the filter doesn't know in advance how much memory to allocate. You would also have issues like whether you allow multi-line strings.
Having said that, it certainly wouldn't be beyond the wit of man...
gzarkadas
8th July 2008, 00:01
I'm now trying to do the reverse of what I was going to do with this thread. Basically I'm implementing a closed caption system, and was planning to use ConditionalReader to do so, but it appears that the only types for ConditionalReader are book, int and float. I'm surprised, I would have thought strings would be easy to implement, e.g.
file.txt
Type string
Default "" (or just nothing)
R 10 180 First Caption
R 185 250 Second Caption
Then simply
ScriptClip("subtitle(myvar)")
ConditionalReader("file.txt", "myvar", false)
Suggestions on how to proceed?
Thx,
Tac
Didn't have time to test, but the following should work:
1. Put all your captions in a multiline string starting with an empty line (that is hit <Enter> after the opening three doublequotes):
global captions = """
First Caption
Second Caption
...
N-th Caption
"""
2. Create a ConditionalReader file of type int and instead of the captions put the indexes to the zero-based array that your multiline string of captions forms (see below):
Type int
Default 0
R 10 180 1
R 185 250 2
3. Treat the multiline string as an AVSLib array with delimiter CRLF. Then at each frame get the caption that corresponds to the specified array index:
LoadLibrary("avslib")
...
#assumes global captions is set by the code-snippet above
...
dummy = ArraySetDelimiter(CRLF)
ScriptClip("subtitle(captions.ArrayGet(myvar))")
ConditionalReader("file.txt", "myvar", false)
Gavino
8th July 2008, 00:54
Nice solution, gzarkadas.
Is there a possible overhead in adding empty subtitles? If so, it might be faster to use
...
ScriptClip("myvar > 0 ? subtitle(captions.ArrayGet(myvar)) : last")
...
gzarkadas
8th July 2008, 07:49
Nice solution, gzarkadas.
Is there a possible overhead in adding empty subtitles? If so, it might be faster to use
...
ScriptClip("myvar > 0 ? subtitle(captions.ArrayGet(myvar)) : last")
...
I doubt it would make a significant difference; thus IMO it is better to keep the code "cleaner" :).
Gavino
8th July 2008, 10:41
I doubt it would make a significant difference; thus IMO it is better to keep the code "cleaner" :).
I too prefer cleaner code over unnecessary optimisation, but I have a feeling that Subtitle has a significant startup cost that would be incurred on every frame, even those where nothing is being written.
Of course, the real test is for tacman1123 to try it out and if it's fast enough for him, then fine. :)
And if he wants most frames to end up with captions, my 'optimisation' won't help anyway. ;)
tacman1123
20th November 2008, 18:41
@gzarkadas: What's the limit on ArrayCreate()? Even using the \ at the end of a line, I run into a problem with a 107 element array (strings total about 2000 characters).
Also, I'm getting the error that ArraySetDelimiter isn't found, is that a different version? I'm using the latest, 1-1-0 beta.
Thanks, this method appears to be working for me, using a global captions=ArrayCreate("one", "two", etc.)
This fails:
LoadLibrary("avslib", CONFIG_AVSLIB_FULL)
global captions=ArrayCreate("Scn 1 (0-19)", \
"Scn 2 (20-29)", \
"Scn 3 (30-31)", \
"Scn 4 (32-51)", \
"Scn 5 (52-54)", \
"Scn 6 (55-55)", \
"Scn 7 (56-56)", \
"Scn 8 (57-76)", \
"Scn 9 (77-99)", \
"Scn 10 (100-100)", \
"Scn 11 (101-101)", \
"Scn 12 (102-103)", \
"Scn 13 (104-109)", \
"Scn 14 (110-110)", \
"Scn 15 (111-111)", \
"Scn 16 (112-113)", \
"Scn 17 (114-114)", \
"Scn 18 (115-115)", \
"Scn 19 (116-123)", \
"Scn 20 (124-146)", \
"Scn 21 (147-176)", \
"Scn 22 (177-202)", \
"Scn 23 (203-204)", \
"Scn 24 (205-206)", \
"Scn 25 (207-208)", \
"Scn 26 (209-210)", \
"Scn 27 (211-212)", \
"Scn 28 (213-214)", \
"Scn 29 (215-216)", \
"Scn 30 (217-218)", \
"Scn 31 (219-220)", \
"Scn 32 (221-222)", \
"Scn 33 (223-224)", \
"Scn 34 (225-226)", \
"Scn 35 (227-228)", \
"Scn 36 (229-230)", \
"Scn 37 (231-231)", \
"Scn 38 (232-232)", \
"Scn 39 (233-243)", \
"Scn 40 (244-245)", \
"Scn 41 (246-246)", \
"Scn 42 (247-247)", \
"Scn 43 (248-253)", \
"Scn 44 (254-259)", \
"Scn 45 (260-273)", \
"Scn 46 (274-294)", \
"Scn 47 (295-295)", \
"Scn 48 (296-296)", \
"Scn 49 (297-298)", \
"Scn 50 (299-299)", \
"Scn 51 (300-300)", \
"Scn 52 (301-301)", \
"Scn 53 (302-303)", \
"Scn 54 (304-304)", \
"Scn 55 (305-305)", \
"Scn 56 (306-306)", \
"Scn 57 (307-307)", \
"Scn 58 (308-338)", \
"Scn 59 (339-341)", \
"Scn 60 (342-343)", \
"Scn 61 (344-344)", \
"Scn 62 (345-345)", \
"Scn 63 (346-346)", \
"Scn 64 (347-348)", \
"Scn 65 (349-349)", \
"Scn 66 (350-350)", \
"Scn 67 (351-351)", \
"Scn 68 (352-352)", \
"Scn 69 (353-353)", \
"Scn 70 (354-355)", \
"Scn 71 (356-356)", \
"Scn 72 (357-357)", \
"Scn 73 (358-358)", \
"Scn 74 (359-362)", \
"Scn 75 (363-364)", \
"Scn 76 (365-366)", \
"Scn 77 (367-367)", \
"Scn 78 (368-368)", \
"Scn 79 (369-371)", \
"Scn 80 (372-372)", \
"Scn 81 (373-373)", \
"Scn 82 (374-374)", \
"Scn 83 (375-375)", \
"Scn 84 (376-376)", \
"Scn 85 (377-379)", \
"Scn 86 (380-380)", \
"Scn 87 (381-381)", \
"Scn 88 (382-383)", \
"Scn 89 (384-384)", \
"Scn 90 (385-386)", \
"Scn 91 (387-390)", \
"Scn 92 (391-391)", \
"Scn 93 (392-393)", \
"Scn 94 (394-394)", \
"Scn 95 (395-395)", \
"Scn 96 (396-397)", \
"Scn 97 (398-398)", \
"Scn 98 (399-399)", \
"Scn 99 (400-400)", \
"Scn 100 (401-401)", \
"Scn 101 (402-413)", \
"Scn 102 (414-429)", \
"Scn 103 (430-433)", \
"Scn 104 (434-500)", \
"Scn 105 (501-509)", \
"Scn 106 (510-576)", \
"Scn 107 (577-6095)")
# return a valid video clip
colorbars.KillAudio()
Gavino
20th November 2008, 19:24
Even using the \ at the end of a line, I run into a problem with a 107 element array (strings total about 2000 characters).
Does it work if you enter the captions as a single multi-line string, as gzarkadas originally suggested in post #12?
global captions = """
First Caption
Second Caption
...
N-th Caption
"""
tacman1123
20th November 2008, 19:37
No, although since I'm having a problem with the SetDelimiter call, I'm using ArrayCreate, e.g. ArrayCreate("one", "two" ...), but it fails as well. I thought at first it might be a line limit length in avisynth scripts, but couldn't find any documentation about that. Figured it'd be best to go with the \ format, since I could post it here without horizontal scrolling.
Gavino
20th November 2008, 19:42
OK, I understand.
What exactly is the failure you are seeing? Do you get any useful error message?
tacman1123
22nd November 2008, 00:04
The error is "Invalid Arguments to Function ArrayCreate()" I imagine there is some limit, and I've exceeded it, but perhaps I can figure out how to merge an array and add them in chunks. I've modified the script above to show the error if you run it, to see it work without the error simply remove a bunch of the middle elements.
A more significant issue is that
a=a.ScriptClip("subtitle(captions.ArrayGet(myvar))")
a.ConditionalReader(root + video_code + "_range.txt", "myvar", false)
starts out fine, but as the video plays it gets slower and slower. Even using something like avs2avi I can see the processed frames per second go down and the estimated time left go up. I don't have the same problem when I put in something like, like myvar or current_frame. Do excessive calls to Captions.ArrayGet() not release memory? Or...? Since it's called every frame, it's obviously being called a lot, but it seems fine the first hundred frames or so, then begins to slow down.
I had an idea about skipping the array and assigning each string a variable, but I can't figure out how to dynamically create a variable made up of a prefix and a number. That is, what I want to do is make scriptclip calls to subtitle(caption1), subtitle(caption2), etc. I can create caption1="one", etc., but am not sure how to get from myvar to "caption" + myvar, and have that new variable be evaluated. I think that's what I want to do.
Tac
tacman1123
22nd November 2008, 00:10
I can recreate the ArrayGet problem without the range issue, here's code that immediately runs slowly for me:
global captions=ArrayCreate("Scn 1 (0-19)", \
"Scn 2 (20-29)", \
"Scn 3 (30-31)", \
"Scn 4 (32-51)", \
"Scn 5 (52-54)", \
"Scn 6 (55-55)", \
"Scn 7 (56-56)", \
"Scn 8 (57-76)", \
"Scn 9 (77-99)", \
"Scn 10 (100-100)", \
"Scn 11 (101-101)", \
"Scn 12 (102-103)", \
"Scn 13 (104-109)")
a=DirectShowSource(my_file)
a=a.ScriptClip("subtitle(captions.ArrayGet(12))")
a
I guessing that accessing higher number elements in the array walks through the array rather than having an internal pointer, and that it's slow. Probably not a problem for scripts, except for runtime ones. Just a theory.
Tac
PS I realize that there are other ways to solve this problem, as Subtitle allows frame ranges, but still an interesting topic about how to solve it with ArrayGet or dynamically generated variable names.
Gavino
22nd November 2008, 00:22
I had an idea about skipping the array and assigning each string a variable, but I can't figure out how to dynamically create a variable made up of a prefix and a number.
Excellent idea! Here's how to do it. Change your ScriptClip line to
a=a.ScriptClip("""subtitle(Eval("caption"+string(myvar)))""")
Gavino
22nd November 2008, 00:26
I guessing that accessing higher number elements in the array walks through the array rather than having an internal pointer, and that it's slow.
I don't use AVSLib myself, but from what I understand, it has to work that way since an array is really just a string in disguise.
EDIT:
PS I realize that there are other ways to solve this problem, as Subtitle allows frame ranges, ...
Actually, you're right - you could just use a whole bunch of Subtitle calls directly. It might be a choice between using less memory (ConditionalReader) and speed (direct).
tacman1123
23rd November 2008, 15:50
Excellent idea! Here's how to do it. Change your ScriptClip line to
a=a.ScriptClip("""s
ubtitle(Eval("caption"+string(myvar)))""")
Between the quotes and parenthesis, this is reminding me of Lisp!
Thanks, this works perfectly.
I've added this solution to the ConditionalReader page on the wiki:
http://avisynth.org/mediawiki/ConditionalReader#Returning_Strings
Look okay?
Tac
Gavino
23rd November 2008, 16:41
Between the quotes and parenthesis, this is reminding me of Lisp!
Not to mention Eval itself... :)
I've added this solution to the ConditionalReader page on the wiki:
http://avisynth.org/mediawiki/ConditionalReader#Returning_Strings
Look okay?
Yes, looks good.
Your example of separate files for different languages is a good practical reason to use this technique.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.