home *** CD-ROM | disk | FTP | other *** search
- /*
- * BuildMPEG, compile a list of images into an MPEG stream
- * using the Stanford codec
- *
- * history
- *
- * 30/06/1994 TEK: Minor change for IFX 2.0
- *
- * 18/04/1994 fixed problems with odd sized pictures
- *
- * 16/04/1994 fixed a few bugs
- *
- * 13/04/1994 added requester that shows why the codec might have failed
- * previously it just guessed memory problems
- * turned redraw off for loading and preprocessing
- *
- * 03/03/1994 added checks to avoid frozen script when the codec never started
- * (bad installation or memory problems)
- *
- * 28/01/1994 allows selection of a filter script to preprocess source frames
- * also cosmetic changes
- *
- * 02/01/1994 tries to handle user abort
- * should definitely work if aborted through 'waiting for activity'
- * period.
- *
- * 01/01/1994 first version
- *
- * Copyright Michael van Elst 1994
- *
- * This program is freely distributable, but copyrighted by me. This means
- * that you can copy it freely as long as you don't ask for any more money
- * than a nominal fee for copying. This program may be put on PD disks,
- * especially on Fred Fish's AmigaLibDisks.
- * This program cannot be used for commercial purposes without permission
- * from the author. The author can not be made responsible for any damage
- * which is caused by using this program.
- *
- * This notice applies to the AREXX script, my changes to the MPEG codec
- * done by the Portable Video Research Group at Stanford and the resulting
- * executable contained in the BuildMPEG archive.
- */
-
- OPTIONS RESULTS
-
- /*logging='>"con:3/12/500/400/MPEG Log/AUTO/WAIT/CLOSE/INACTIVE"'*/
- logging=''
- /*profiling='sc:c/lprof -t5'*/
- profiling=''
-
- cleanup = nothing
- SIGNAL ON BREAK_C
-
- CALL ADDLIB("rexxsupport.library",0,-30,0)
-
- /**************************************
- * Added by TEK 6/30/94:
- */
- GetStatus AssignDir /* IFX2.0: gives us it's assign */
- IF rc = 0 THEN
- assign = result
- ELSE
- assign = "IMAGEFX:"
- /**************************************/
-
- /* TEK 11/15/94: */
- SetPrefs SaveNails Off
-
- CALL DoRequesters
-
- CALL MakeOptions
-
- CALL MainLoop
-
- /* TEK 11/15/94: */
- SetPrefs SaveNails On
-
- EXIT
-
- /*-------------------------------------------------------------------------
- *
- * auxiliary functions
- *
- *-------------------------------------------------------------------------*/
-
- /*
- * check if argument is a positive integer
- * if not then return default
- */
- NumDefault:
- procedure
- parse arg input,low,high,default
-
- if ~datatype(input,'w') | input<low | input>high
- then return default
- else return input
-
- /*
- * display an information requester with no special symbols in strings
- */
- MyNotify:
- procedure
- parse arg prompt
-
- string = ""
- do until i=0
- i = pos('%',prompt)
- if i>0 then do
- string = string || left(prompt,i) || '%'
- prompt = substr(prompt,i+1)
- end
- else
- string = string || prompt
- end
-
- requestnotify string
- return
-
- /*
- * ensure reasonable defaults
- */
- EnsureDefaults:
- bitrate = numdefault(bitrate,0,100000,1200)
- ffirst = numdefault(ffirst,0,999999,1)
- flast = numdefault(flast,0,999999,10)
- finterval = numdefault(finterval,0,99,3)
- ginterval = numdefault(ginterval,0,99,2)
- framerate = numdefault(framerate,1,8,3)
- targetsize = numdefault(targetsize,0,10000000,0)
- xingflag = numdefault(xingflag,0,1,0)
- msdiameter = numdefault(msdiameter,1,15,15)
- intramode = numdefault(intramode,0,1,0)
- precisedct = numdefault(precisedct,0,1,0)
- telescope = numdefault(telescope,0,1,1)
- bounding = numdefault(bounding,0,1,0)
- mvpredict = numdefault(mvpredict,0,1,0)
- quantizer = numdefault(quantizer,0,31,0)
- return
-
- /*
- * format a positive integer to have at least n digits
- */
- MakeDigits:
- procedure
- arg v,n
-
- l = length(v)
- do while l<n
- v = "0"v
- l = l+1
- end
-
- return v
-
- /*
- * trim quotes from a string
- */
- TrimQuotes:
- procedure
- parse arg in
-
- l = length(in)
-
- if l>1 & left(in,1)='"' & right(in,1)='"'
- then out = substr(in,2,l-2)
- else out = in
-
- return out
-
- /*-------------------------------------------------------------------------
- *
- * parameter requesters
- *
- *-------------------------------------------------------------------------*/
-
- DoRequesters:
-
- /*
- * fetch last parameters
- */
- inpattern = GETCLIP('MPEG_In')
- ffirst = GETCLIP('MPEG_FirstFrame')
- flast = GETCLIP('MPEG_LastFrame')
- outfile = GETCLIP('MPEG_Out')
- finterval = GETCLIP('MPEG_FInterval')
- ginterval = GETCLIP('MPEG_GInterval')
- framerate = GETCLIP('MPEG_Framerate')
- bitrate = GETCLIP('MPEG_Bitrate')
- targetsize = GETCLIP('MPEG_Targetsize')
- xingflag = GETCLIP('MPEG_Xingflag')
- cscript = GETCLIP('MPEG_Controlscript')
- msdiameter = GETCLIP('MPEG_MSDiameter')
- intramode = GETCLIP('MPEG_Intramode')
- precisedct = GETCLIP('MPEG_PreciseDCT')
- telescope = GETCLIP('MPEG_MVTelescoping')
- bounding = GETCLIP('MPEG_DMVBounding')
- mvpredict = GETCLIP('MPEG_MVPrediction')
- quantizer = GETCLIP('MPEG_Quantization')
- fscript = GETCLIP('MPEG_FilterScript')
-
- CALL EnsureDefaults
-
- fpsstring = '23.51fps 24fps 25fps 25.50fps 30fps 50fps 59.94fps 60fps'
- if framerate>1 then
- gadstring = subword(fpsstring,framerate,words(fpsstring)-framerate+1) || ,
- " " || subword(fpsstring,1,framerate-1)
- else
- gadstring = fpsstring
- gadstring = translate(gadstring,"/"," ")
-
- Gadget.1 = 'S/125/20/Input Pattern:/'inpattern
- Gadget.2 = 'I/330/20/From:/'ffirst
- Gadget.3 = 'I/430/20/To:/'flast
- Gadget.4 = 'S/125/35/Output Filename:/'outfile
- Gadget.5 = 'I/125/50/Frame Interval:/'finterval
- Gadget.6 = 'I/125/65/Group Interval:/'ginterval
- Gadget.7 = 'C/430/35/ /8/'gadstring
- Gadget.8 = 'L/348/38/1/1/Frame rate:'
- Gadget.9 = 'I/430/50/Bit rate:/'bitrate
- Gadget.10 = 'I/430/65/Target size:/'targetsize
- Gadget.11 = 'X/85/82/Ă—ING Override/'xingflag
- Gadget.12 = 'X/255/82/Query Advanced Options.../0'
-
- Extras.1 = 'X/20/20/DC intraframe mode/'intramode
- Extras.2 = 'X/20/35/Use Precise DCT/'precisedct
- Extras.3 = 'S/130/80/Control Script:/'cscript
- Extras.4 = 'X/280/20/Motion Vector Telescoping/'telescope
- Extras.5 = 'X/280/35/Dynamic Motion Vector Bounding/'bounding
- Extras.6 = 'X/280/50/Motion Vector prediction/'mvpredict
- Extras.7 = 'I/420/65/Search diameter:/'msdiameter
- Extras.8 = 'I/420/80/Quantization:/'quantizer
- Extras.9 = 'S/130/65/IFX Filter:/'fscript
-
- ComplexRequest '"MPEG Compiler"' 12 Gadget 540 120
- IF rc ~= 0 then EXIT
-
- /* fetch parameters back from requester */
- inpattern = result.1
- ffirst = result.2
- flast = result.3
- outfile = result.4
- finterval = result.5
- ginterval = result.6
- fpsindex = result.7
- bitrate = result.9
- targetsize = result.10
- xingflag = result.11
- advanced = result.12
-
- framerate = (fpsindex + (framerate-1)) // words(fpsstring) + 1
-
- if advanced then do
- ComplexRequest '"MPEG Advanced Options"' 9 Extras 540 120
- if rc ~= 0 then EXIT
- /* fetch parameters back from requester */
- intramode = result.1
- precisedct = result.2
- cscript = result.3
- telescope = result.4
- bounding = result.5
- mvpredict = result.6
- msdiameter = result.7
- quantizer = result.8
- fscript = result.9
- end
-
- CALL EnsureDefaults
-
- CALL SETCLIP('MPEG_In', inpattern)
- CALL SETCLIP('MPEG_FirstFrame', ffirst)
- CALL SETCLIP('MPEG_LastFrame', flast)
- CALL SETCLIP('MPEG_Out', outfile)
- CALL SETCLIP('MPEG_FInterval', finterval)
- CALL SETCLIP('MPEG_GInterval', ginterval)
- CALL SETCLIP('MPEG_Framerate', framerate)
- CALL SETCLIP('MPEG_Bitrate', bitrate)
- CALL SETCLIP('MPEG_Targetsize', targetsize)
- CALL SETCLIP('MPEG_Xingflag', xingflag)
- CALL SETCLIP('MPEG_Controlscript', cscript)
- CALL SETCLIP('MPEG_MSDiameter', msdiameter)
- CALL SETCLIP('MPEG_Intramode', intramode)
- CALL SETCLIP('MPEG_PreciseDCT', precisedct)
- CALL SETCLIP('MPEG_MVTelescoping', telescope)
- CALL SETCLIP('MPEG_DMVBounding', bounding)
- CALL SETCLIP('MPEG_MVPrediction', mvpredict)
- CALL SETCLIP('MPEG_Quantization', quantizer)
- CALL SETCLIP('MPEG_FilterScript', fscript)
-
- /*
- * split input pattern
- */
-
- csep = pos(':',inpattern)
- ssep = lastpos('/',inpattern)
- if ssep>0 & ssep>csep then do
- directory = left(inpattern,ssep)
- infilename = substr(inpattern,ssep+1)
- end
- else if csep>0 & csep>ssep then do
- directory = left(inpattern,csep)
- infilename = substr(inpattern,csep+1)
- end
- else do
- directory = ""
- infilename = inpattern
- end
-
- csep = pos('%',infilename)
- if csep>0 then do
- ssep=csep+1
- do while substr(infilename,ssep,1)='%'
- ssep = ssep+1
- end
- inprefix = left(infilename,csep-1)
- insuffix = substr(infilename,ssep)
- indigits = ssep-csep
- end
- else do
- inprefix = infilename
- insuffix = ""
- indigits = 1
- end
-
- return
-
- /*-------------------------------------------------------------------------
- *
- * run a user command or script
- *
- *-------------------------------------------------------------------------*/
-
- UserScript:
-
- if pos(':',fscript)>0 then
- filterfile = fscript
- else
- filterfile = assign||"mpegfilters/"fscript
-
- if open('filter',filterfile,'r') then do
- rx quiet filterfile
- call close('filter')
- end
- else if open('filter',filterfile'.ifx','r') then do
- rx quiet filterfile".ifx"
- call close('filter')
- end
- else
- rxs fscript
-
- return
-
- /*-------------------------------------------------------------------------
- *
- * automatically preformat for XING files
- *
- *-------------------------------------------------------------------------*/
-
- XingPreProc:
-
- GetMain ; if result="" then EXIT
- parse VAR result name width height depth .
- if width ~= 160 | height ~= 120 then
- scale 160 120
-
- return
-
-
- /*-------------------------------------------------------------------------
- *
- * create command line options for Stanford CODEC
- *
- *-------------------------------------------------------------------------*/
-
- MakeOptions:
-
- command = assign||"mpeg/mpeg"
- opts = "-PF" "-a" ffirst "-b" flast
-
- check=statef(command)
- parse var check typ len blocks perm .
- if typ ~= "FILE" | substr(perm,7,1) ~= 'E' then do
- call MyNotify("Sorry, can't find "command" or wrong permissions.")
- EXIT
- end
-
- if xingflag then
- opts = opts "-XING"
- else do
- if cscript ~= "" then do
- if pos(':',cscript)>0 then
- controlfile = cscript
- else
- controlfile = assign||"mpegcontrol/"cscript
- command = command '<"'controlfile'"'
- opts = opts "-o"
- end
- if ~telescope then opts = opts "-NVNT"
- if bounding then opts = opts "-DMVB"
- if intramode then opts = opts "-4"
- if mvpredict then opts = opts "-c"
- if precisedct then opts = opts "-y"
- if finterval>0 then opts = opts "-f" finterval
- if ginterval>0 then opts = opts "-g" ginterval
- if msdiameter>0 then opts = opts "-i" msdiameter
- if bitrate>0 then opts = opts "-r" bitrate*1024
- if framerate>0 then opts = opts "-p" framerate
- if quantizer>0 then opts = opts "-q" quantizer
- if targetsize>0 then opts = opts "-x" targetsize*8192
- end
-
- id = random(100,999,time('s'))
- pipename = "PIPE:"address()"."id"="
- portname = "IFX_BuildMPEGServer."id
- statfile = 'T:CODE.return_status.'id
-
- opts = opts "-REXX" portname||":"||mpeg
-
- if outfile = "" then do
- requestnotify "Sorry, you must given an output filename"
- EXIT
- end
-
- /*
- * preload first frame to determine size or
- * to check size against XING values
- */
- lastbuf = directory||inprefix||makedigits(ffirst,indigits)||insuffix
- redraw off
- loadbuffer lastbuf FORCE
- if rc>0 then do
- redraw on
- requestnotify "Failed to preload first frame"
- EXIT 10
- end
- CALL UserScript
- if xingflag then CALL XingPreProc
- redraw on
-
- GetMain ; if result="" then EXIT
- parse VAR result name width height depth .
- /* round size up, YUVSPLIT does the same */
- if (width // 2) > 0 then width = width + 1
- if (height // 2) > 0 then height = height + 1
-
- if xingflag then
- do
- if width ~= 160 | height ~= 120 then do
- requestnotify "XING frames need to be of size 160x120 pixel"
- EXIT
- end
- end
- else
- opts = opts "-h" width "-v" height
-
- opts = opts "-s" '"'outfile'"' '"'pipename'"'
-
- mpegcommand = command opts
-
- return
-
- /*-------------------------------------------------------------------------
- *
- * run the codec program and feed it with data
- *
- *-------------------------------------------------------------------------*/
-
- MainLoop:
-
- LockInput
- Undo Off
-
- cleanup = "restoreifx"
-
- nullptr = '00000000'x
-
- port = openport(portname);
- if port = nullptr then do
- call MyNotify("can't create server port '" portname "'")
- exit
- end
-
- packet = "xx"
- cleanup = "flushcodec"
- CALL SETCLIP('MPEG_PipeName',"")
- message "Running CODEC in the background"
- pre = 'failat 99'
- post1 = 'get RC >'statfile
- post2 = 'rx "address '''portname''' x FAIL"'
- sep = '+'x2c('0a')
- address command "run" logging pre sep profiling mpegcommand sep post1 sep post2
- if rc>0 then do
- call MyNotify("can't create background process")
- exit
- end
- framenumber = 1
-
- do until subcommand = "FAIL"
- cleanup = "flushcodec"
- message "Waiting for activity on frame "framenumber
- call waitpkt portname
- do until packet=nullptr
- packet = getpkt(portname);
- if packet ~= nullptr then
- do
- command = getarg(packet,0)
-
- call reply packet, 0 /* should be atomic */
- packet = nullptr
-
- parse var command prefix subcommand filespec x y .
- CALL SETCLIP('MPEG_PipeName',filespec)
- select
- when subcommand = "LoadMem" then do
- /* filespec is something like: PIPE:ident.random=framenr.Y */
- csep = lastpos('=',filespec)
- ssep = lastpos('.',filespec)
- filetype = substr(filespec,ssep+1,1)
- framenumber = substr(filespec,csep+1,ssep-csep-1)
- framenumber = makedigits(framenumber,indigits)
-
- message "pushing "filetype" data for frame "framenumber
-
- newbuf = directory||inprefix||framenumber||insuffix
- if lastbuf ~= newbuf then do
- redraw off
- loadbuffer newbuf FORCE
- if rc>0 then do
- requestnotify "Failed to read frame"||framenumber
- SIGNAL BREAK_C
- end
- CALL UserScript
- if xingflag then CALL XingPreProc
- redraw on
- lastbuf = newbuf
- end
- GetMain ; if result="" then SIGNAL BREAK_C
- parse VAR result name width height depth .
- /* round size up, YUVSPLIT does the same */
- if (width // 2) > 0 then width = width + 1
- if (height // 2) > 0 then height = height + 1
- if filetype ~= 'Y' then do
- width = width/2
- height = height/2
- end
-
- /* this check fails when the framesize is not constant
- * or a load failed (then width=height=0)
- */
- if width ~= x | height ~= y then do
- /* then we push an empty file which will
- * make the codec abort
- */
- filename = trimquotes(filespec)
- call open('push',filename,'w')
- call writeln('push',"STOP") /* some dummy data */
- call close('push')
- end
- else
- savebufferas YUVSPLIT filespec filetype
- end
- when subcommand = "EXIT" then
- cleanup = "restoreifx"
- when subcommand = "FAIL" then do
- end
- otherwise
- call MyNotify("unknown command" subcommand)
-
- end
- end
- end
- end
-
- code="Out of memory ?"
- if open('result',statfile,'r') then do
- code = readln('result')
- call close('result')
- select
- when code=0 then code=0
- when code=1 then code="Input values out of bounds."
- when code=2 then code="Huffman decoder finds bad code."
- when code=3 then code="Undefined value in encoder."
- when code=4 then code="Error found in Marker."
- when code=5 then code="Cannot initialize MPEG stream."
- when code=6 then code="No recovery mode specified."
- when code=7 then code="End of file unexpected."
- when code=8 then code="Bad marker structure."
- when code=9 then code="Cannot write output."
- when code=10 then code="Cannot read input."
- when code=11 then code="System parameter Error."
- when code=12 then code="Memory allocation failure."
- otherwise code="General failure."
- end
- end
- if code~=0 then call MyNotify("MPEG encoder failed. "||code)
- message "done"
-
- call closeport(port);
- address command "delete force file " statfile
-
- UnlockInput
- Undo On
-
- return
-
- /*-------------------------------------------------------------------------
- *
- * cleaning up
- *
- *-------------------------------------------------------------------------*/
-
- BREAK_C:
-
- /*
- * beware.. we can't handle another break_c at this point
- */
-
- select
- when cleanup="restoreifx" then do
- call closeport(port)
- UnlockInput
- Undo On
- end
- when cleanup="flushcodec" then do
- call closeport(port) /* let all requests fail immediatly */
- if (filespec ~= "") then do
- filename = trimquotes(filespec)
- if ~open('tryme',filename,'r') then do
- if open('push',filename,'w') then do
- call writeln('push',"STOP") /* some dummy data */
- call close('push')
- end
- else
- requestnotify "pushing data failed. Possible Hangup."
- end
- else do
- call close('tryme')
- end
- end
- UnlockInput
- Undo On
- end
- otherwise
- NOP
- end
-
- EXIT
-