Log in

View Full Version : Make a transcoder using avisynth


FranceBB
23rd June 2016, 00:40
Hi!
(This time I'm not actually sure whether it's the right section or not)
The company I'm working for decided to switch to an internal transcoder (instead of using external transcoders like Akamai etc) and, since I was talking about avisynth, they asked me to set up one.
They basically would like a folder in which they can "move" files (any kind of files) and that automatically starts a transcoding, and creates a 1920x1080i 25i PAL interlaced 4:2:2 8bit 50,0 Mbit/s MPEG-2 output file, which has 8 unpaired mono .wav tracks.
Despite the fact that I've been using avisynth for years, I have no idea about how to make an automated solution like the one they asked me to make.
Of course, even though "any kind of files" it's not exactly specific, I assume they would like to convert files like .mxf avc-i, .mxf avc, .mxf avc s, .mts avchd, .avi CanopusHQ, .mov AppleProRes, .mov Avid DNxHR, .mov XF-AVC etc which should be supported by either FFMpeg or LWLibav.
As to the fact that they all need to be interlaced, I was thinking about using either tdeint or QTGMC followed by something like:

ConvertFPS(50)
assumeTFF()
separatefields()
selectevery(4,0,3)
weave()

so that if it's NTSC, it will deinterlace it and then re-interlace it in PAL, if it's PAL it will deinterlace it and then re-interlace it in PAL and if it's progressive (23,976 fps for instance), it will create blended frame to convert it to 25 fps and then interlace it in PAL.
Anyway, I would like your help about the automation part and as many suggestions as possible.
For instance, is it possible to write something like a "try-catch" which uses FFmpeg if LwLibav fails (or something like that as "failover" solution)?
Last but not least, it should be able to run on Windows Server 2003 (but it's not mandatory, since they have some machines running Windows Server 2008 R2 as well).

Thank you in advance.

StainlessS
23rd June 2016, 00:55
Maybe for some ideas

Function GetFile(string vFN,string "aFN",Int "Prefer",Float "AudioDelay") {
myName="GetFile: "
START=RT_TimerHP
Assert(vFN!="",myName+"Empty VideofileName")
vFN = LCase(vFN)
aFN = LCase(Default(aFN,""))
Prefer=Default(Prefer,0) # Default 0=Avi if "AVI", 1, 1 = L-SMASH, 2 = FFMPegSource
Prefer = (Prefer<0 || Prefer>2) ? 0 : Prefer
AudioDelay=Float(Default(AudioDelay,0.0))
SepAud = (aFN!="" && aFN!=vFN)
Ext=RT_GetFileExtension(vFN)
Function IsISOFileName(String s) {
s=Lcase(RT_GetFileExtension(s)) Return(s==".mov"||s==".mp4"||s==".3gp"||s==".3g2"||s==".mj2"||s==".dvb"||s==".dcf"||s==".m21")}
GScript("""
c = 0
FLGS = 0
RT_DebugF("\nOpening Video %s for Extension %s",vFN,Ext,name=myName)
try {
# Attempt open for some formats by extension, NOT AVISource as could be non seekable, Allow AVI Indexing via later Source Plugs.
if(Ext == ".d2v") {c = MPEG2Source(vFN,UPCONV=1) RT_DebugF("MPEG2Source Succeeds:",name=myName)}
Else if(Prefer==0 && Ext == ".avi") {
c = AviSource(vFN)
try {c=AviSource(vFN) RT_DebugF("AVISource Video Succeeds:",name=myName) }
catch (msg) {RT_DebugF("AVISource Video Fails:- '%s'",msg,name=myName) }
}
if(!c.IsClip) {
if(Prefer<=1) {
If(IsISOFileName(Ext)) {
FLGS=RT_BitSet(FLGS,0)
c = LSMASHVideoSource(vFN) RT_DebugF("LSMASHVideoSource Video Succeeds:",name=myName)
if(!SepAud) {FLGS=RT_BitSet(FLGS,1) try{c=AudioDubEx(c,LSMASHAudioSource(vFN)) } catch(msg){} }
}
if(!c.IsClip) {
try {FLGS=RT_BitSet(FLGS,2) c=LWLibavVideoSource(vFN) RT_DebugF("LWLibavVideoSource Video Succeeds:",name=myName)
if(!SepAud) {FLGS=RT_BitSet(FLGS,3) try{c=AudioDubEx(c,LWLibavAudioSource(vFN)) } catch(msg){} }
} catch (msg) {RT_DebugF("LWLibavVideoSource Video Fails:- '%s'",msg,name=myName) }
}
} Else {
try {FLGS=RT_BitSet(FLGS,4) FFIndex(vFN) c=FFVideoSource(vFN) RT_DebugF("FFMSSource Video Succeeds:",name=myName)
if(!SepAud) {FLGS=RT_BitSet(FLGS,5) try{c=AudioDubEx(c,FFAudioSource(vFN)) } catch(msg){} }
} catch(msg) {RT_DebugF("FFMSSource Video Fails:- '%s'",msg,name=myName)}
}
}
} catch (msg) {c=0 RT_DebugF("Open Video by Extension Fails:- '%s'",msg,name=myName)}

if(!c.IsClip) {
if(!RT_BitTst(FLGS,4)) {
try {FLGS=RT_BitSet(FLGS,4) FFIndex(vFN) c=FFVideoSource(vFN) RT_DebugF("FFMSSource Video Succeeds:",name=myName)
if(!SepAud) {FLGS=RT_BitSet(FLGS,5) try{c=AudioDubEx(c,FFAudioSource(vFN)) } catch(msg){} }
} catch(msg) {RT_DebugF("FFMSSource Video Fails:- '%s'",msg,name=myName)}
}
if(!c.IsClip) {
if(!RT_BitTst(FLGS,2)) {
try {FLGS=RT_BitSet(FLGS,2) c=LWLibavVideoSource(vFN) RT_DebugF("LWLibavVideoSource Video Succeeds:",name=myName)
if(!SepAud) {FLGS=RT_BitSet(FLGS,3) try{c=AudioDubEx(c,LWLibavAudioSource(vFN)) } catch(msg){} }
} catch (msg) {RT_DebugF("LWLibavVideoSource Video Fails:- '%s'",msg,name=myName) }
}
if(!c.IsClip) {
if(!RT_BitTst(FLGS,0)) { # Not tried LSmash Video yet
FLGS=RT_BitSet(FLGS,0)
try {c = LSMASHVideoSource(vFN) RT_DebugF("LSMASHVideoSource Video Succeeds:",name=myName)
if(!SepAud) {FLGS=RT_BitSet(FLGS,1) try{c=AudioDubEx(c,LSMASHAudioSource(vFN)) } catch(msg){} }
} catch (msg) {RT_DebugF("LSMASHVideoSource Video Fails:- '%s'",msg,name=myName) }
}
if(!c.IsClip) {
try {c = DSS2(vFN) RT_DebugF("DSS2Source Video Succeeds:",name=myName) }
catch(msg) {RT_DebugF("DSS2Source Video Fails:- '%s'",msg,name=myName)
if(Ext==".avi") { # One last go before DirectShowSource
try {c=AviSource(vFN) RT_DebugF("AVISource Video Succeeds:",name=myName) }
catch (msg) {RT_DebugF("AVISource Video Fails:- '%s'",msg,name=myName) }
}
if(!c.IsClip) {
try {c=DirectShowSource(vFN,pixel_type="YV12") RT_DebugF("DirectShowSource(YV12) Video Succeeds:",name=myName) }
catch(msg) {RT_DebugF("DirectShowSource(YV12) Video Fails:- '%s'",msg,name=myName)
try {c=DirectShowSource(vFN,pixel_type="YUY2") RT_DebugF("DirectShowSource(YUY2) Video Succeeds:",name=myName) }
catch(msg) {RT_DebugF("DirectShowSource(YUY2) Video Fails:- '%s'",msg,name=myName)
try {c=DirectShowSource(vFN,pixel_type="RGB") RT_DebugF("DirectShowSource(RGB) Video Succeeds:",name=myName) }
catch(msg) {Assert(False,myName+"DirectShowSource(RGB) Video Fails: "+Chr(10)+msg)}
}
}
}
}
if(!c.HasAudio && !SepAud) {try{c=AudioDubEx(c,DirectShowSource(vFN,video=false) ) } catch(msg){} }
}
}
}
}

if((!c.HasAudio&&Ext!=".d2v") || SepAud){
RT_DebugF("Attempting Get Audio")
a = 0
aFN = (aFN=="") ? vFN : aFN
Ext=RT_GetFileExtension(aFN)
if(SepAud) {
RT_DebugF("Opening Audio %s for Extension %s",aFN,Ext,name=myName)
try {
if(Ext==".ac3") {a=NICAC3Source(aFN,channels=2,DRC=0) RT_DebugF("NICAC3Source Succeeds:",name=myName) }
Else if(Ext==".mpa"||Ext==".mp1"||Ext==".mp2"||Ext==".mp3") {
a=NicMPG123Source(aFN,Normalize=False) RT_DebugF("NicMPG123Source Succeeds:",name=myName) }
Else if(Ext==".wav") {a=RaWavSource(afN) RT_DebugF("RaWavSource Succeeds:",name=myName) }
Else if(Ext==".dts") {a=NicDTSSource(aFN) RT_DebugF("NicDTSSource Succeeds:",name=myName)}
} catch (msg) {a=0 RT_DebugF("Extension based Audio Fails:- '%s'",msg,name=myName) }
}
if(!a.IsClip) {
if(SepAud || !RT_BitTst(FLGS,5)) {
RT_DebugF("Attempt open Audio with FFAudioSource",name=myName)
try {
if(SepAud || !RT_BitTst(FLGS,4)) {FFIndex(aFN)}
a = FFAudioSource(aFN) RT_DebugF("FFAudioSource Succeeds:",name=myName)
} catch(msg) {RT_DebugF("FFAudioSource Fails:- '%s'",msg,name=myName)}
}
if(!a.IsClip) {
if((SepAud || !RT_BitTst(FLGS,3))) {
RT_DebugF("Attempt open Audio with LWLibavAudioSource",name=myName)
try {a=LWLibavAudioSource(aFN) RT_DebugF("LWLibavAudioSource Succeeds:",name=myName) }
catch (msg) {RT_DebugF("LWLibavAudioSource Fails:- '%s'",msg,name=myName) }
}
if(!a.IsClip) {
if((SepAud || !RT_BitTst(FLGS,1))) {
RT_DebugF("Attempt open Audio with LSMASHAudioSource",name=myName)
try {a=LSMASHAudioSource(aFN) RT_DebugF("LSMASHAudioSource Succeeds:",name=myName) }
catch (msg) {RT_DebugF("LSMASHAudioSource Fails:- '%s'",msg,name=myName) }
}
if(!a.IsClip && SepAud) {
RT_DebugF("Attempt open Audio with DirectShowSource",name=myName)
try {a=DirectShowSource(s,video=false) RT_DebugF("DirectShowSource Succeeds:",name=myName) }
catch (msg) {a=0 Assert(aFN!=vFN,myName+"Audio Fails: "+Chr(10)+msg)}
}
}
}
}
c = (a.IsClip) ? AudioDubEx(c,a) : c
c = (c.HasAudio) ? DelayAudio(c,AudioDelay).Trim(0,0) : c

}

RT_DebugF("Checking dimensions")
W=c.Width % 8 H=c.Height % 8
if(W > 0 || H > 0) {
W=(W==0)?0:8-W H=(H==0)?0:8-H
if(c.IsRGB()) {
yMin=c.RT_YPlaneMin(n=0,threshold=0.2) # Default matrix @ PC levels
Col = (yMin * 256 + yMin) * 256 + yMin
RT_DebugF("RGB Adding Borders %dx%d, Y=$%06X",W,H,Col,name=myName)
c=c.Addborders(0,0,W,H,Col)
} Else {
yMin=c.RT_YPlaneMin(n=0,threshold=0.2)
Col = (yMin * 256 + $80) * 256 + $80
RT_DebugF("YUV Adding Borders %dx%d, Y=$%06X",W,H,Col,name=myName)
if(W>0) {c=c.StackHorizontal(c.Blankclip(color_yuv=Col,Width=W))}
if(H>0) {c=c.StackVertical(c.Blankclip(color_yuv=Col,Height=H))}
}
}
RT_DebugF("ConvertYV12")
c = c.ConvertToYV12()
RT_DebugF("Convert Stereo")
c = FAVCStereo(c,c) # Output is YV12 + Stereo, Width & Height Mod 8
""")
T=RT_TimerHP - START
RT_DebugF("Total File Open Time = %.2fsecs",T,name=myName)
return c
}


Function GetSeg(string s) {
Return GetFile(s).DoSeg()
}


Work in progress

EDIT: Intending to use some kind of MediaInfo together with above.
EDIT: take above comments with a pinch of salt.

FranceBB
24th June 2016, 00:32
Thank you very much indeed for that! :D

MysteryX
24th June 2016, 02:32
For a solution that integrates into Windows to do automated work, I definitely wouldn't code it in AviSynth. AviSynth is for doing the actual video processing. You'll also need ffmpeg and a bunch of logic to move files around and muxe files.

IMO you'll need to use a programming language (such as C#) to create the interface and logic and that will call AviSynth and ffmpeg as needed.

but then StainlessS seems to view things differently!

FranceBB
25th June 2016, 15:50
Looks like someone made what I was looking for already and it's called "FFAStrans":

by steipal » Wed Feb 12, 2014 9:16 pm

Hi all!

I have just released an unattended transcoding system based around Zeranoes FFmpeg builds. It's called FFAStrans and it does multiple watch folder transcoding with the ability to use AviSynth for filtering. Here's some of it's features:

- Multiple simultaneous workflows with different configurations, each run in separate processes
- Insert video or stills images
- Convert images into video
- Overlay watermark or transparent video
- Add superimposed time code just like real broadcast VTR's
- Insert custom AviSynth script
- Create custom FFMpeg based encoder profile
- Execute any dos command within workflow
- Global support for time, date and system environment variables
- Set up simple transcoding farms
- 100% portable, no installation. All dependencies included

It's still beta and a few features are not yet properly implemented but please visit FFAStrans if you find it interesting. FFAStrans is actually beeing developed, tested and used 24/7 in a real, national broadcasting environment.
BIG thanks to Zeranoe for the great FFmpeg builds! :D

StainlessS
26th June 2016, 15:39
I take it that you've seen this (last updated about 2 weeks ago):- http://www.ffastrans.com/
(your quote dated about 2 years ago).

FranceBB
26th June 2016, 17:03
@StainlessS... yep; I didn't post the link 'cause I wasn't sure whether I was allowed or not.
Anyway, I saved your function as well and I kinda like it.
I may not use it this very moment for the company I'm working, but I'll probably figure out how to make a good use of it. (In other words, you didn't write it for nothing, I promise).

Thank you, really. (and not just for that, but for filters you wrote and useful replies I read here and there on doom9 as well ;) )