Tuesday, January 8, 2008

CFEXECUTE, FFMPEG and MENCODER - Mystery Solved

I am feeling good today. Why? Because I think I have solved the mystery of how to make cfexecute and ffmpeg and/or mencoder work together on ColdFusion 8. That question has been bugging me for a few weeks now. Now the fact that this problem was really solved by Ben Forta, et. al., long before now, is irrelevant ;) I think my perseverance has earned me a gold star anyway.

The Rosetta Stone

In previous entries I described how I was able to use ffmpeg and mencoder with Runtime.exec and cfthread. So far it is working well and one seeming advantage over cfexecute is the ability to kill the process if it exceeds a certain amount of time. But ever curious I still wanted to know if it could be done with cfexecute. Well, thanks to a helpful suggestion from halL on livedocs.adobe.com, and the wisdom of Ben Forta, I have discovered it can be done.

Waiting for Godot

The solution was found in two entries on Ben Forta's blog. The first key was using using /c as an argument. On windows, this "tells the command interpreter to run and terminate upon completion". Otherwise the interpreter may continue to run, waiting endlessly for the signal it should finish.

The second entry mentioned a quirk of cfexecute: it only captures the stdout stream, not stderr. This is important to programs like ffmpeg and mencoder because they write to both streams. According to Forta you can capture both streams by redirecting stderr to stdout. On windows this is done by appending 2>&1 to the end of the command to be executed.

Nirvana (No not *that* Nirvana)

Now I had seen both tips before. I could have sworn I had tried them and they did not work with ffmpeg. I was half-right. The problem was I had used the options separately, not together.

When I tried using the /c flag only, cfexecute timed out. When I examined the Task Manager I could see it left an orphaned cmd.exe and ffmpeg.exe process running. They continued to run until I bounced the CF server.


<!---
WARNING!!!
This code will leave two instances of CMD.exe and
FFMPEG.EXE running until the CF server is restarted
--->
<cftry>
<!--- using a large file and a short timeout value --->
<cfset argString = '/c d:\bin\ffmpeg.exe -i "d:\bin\testInput.mpg" -g 300 -y -s 300x200 -f flv -ar 44100 "d:\bin\testOuput12.flv"' >
<cfexecute name="c:\winnt\system32\cmd.exe" arguments="#argString#" outputFile="d:\bin\results12.log" timeout="120" />

<cfcatch>
<cfdump var="#cfcatch#">
</cfcatch>
</cftry>


Next I tried using the redirect option, but again cfexecute timed out. This time it left behind an orphaned cmd.exe process. Or perhaps I have so many orphaned processes running right now, I have lost track. Whichever processes it left behind, they too remained running until the CF server was restarted.


<!---
WARNING!!!
This code will leave an instance of CMD.exe and
running until the CF server is restarted
--->

<cftry>
<!--- using a large file and a short timeout value --->
<cfset argString = 'd:\bin\ffmpeg.exe -i "d:\bin\testInput.mpg" -g 300 -y -s 300x200 -f flv -ar 44100 "d:\bin\testOuput13.flv" 2>&1' >
<cfexecute name="c:\winnt\system32\cmd.exe" arguments="#argString#" outputFile="d:\bin\results13.log" timeout="120" />

<cfcatch>
<cfdump var="#cfcatch#">
</cfcatch>
</cftry>


Finally it hit me. I had not tried using both options together. Doh! So I added the options, clicked my heels three times, muttered an incantation and .. poof! Once I used both the /c and redirect 2>&1 options, ffmpeg and mencoder worked perfectly.


<!---
GOOD:
Use both the /c flag and the redirect 2>&1 option
--->
<cftry>
<!--- using a large file and a short timeout value --->
<cfset argString = '/c d:\bin\ffmpeg.exe -i "d:\bin\testInput.mpg" -g 300 -y -s 300x200 -f flv -ar 44100 "d:\bin\testOuput14.flv" 2>&1' >
<cfexecute name="c:\winnt\system32\cmd.exe" arguments="#argString#"
outputFile="d:\bin\results14.log" timeout="120" />

<cfcatch>
<cfdump var="#cfcatch#">
</cfcatch>
</cftry>


Now if for some reason cfexecute times out, it may leave the processes running temporarily. But since we have used the /c flag, they will terminate eventually, once they have completed. That is one reason I am leaning towards the Runtime.exec method over cfexecute. The ability to kill a long running process appeals to me. Though now that I think about it, a "killOnTimeout" attribute might be a nice enhancement of cfexecute. But I will wait to see what future versions bring.

That is it for now. Comments, corrections or suggestions are always welcome. Enjoy!

11 comments:

Anonymous,  February 28, 2008 at 8:01 AM  

I was actually struggling with much the same problem, and when I figured it out I posted the solution hoping that others could use it to. I'm glad those posts helped you with this one.

--- Ben

cfSearching February 28, 2008 at 8:36 AM  

I am glad you did. Those two posts were the missing puzzle pieces here. Very much appreciated.

Anonymous,  March 28, 2008 at 1:03 PM  

I've been struggling with the same problem for a weeks. Thanks a lot for the explanation. By the way last code is missing arguments="#argString#" in cfexecute

cfSearching March 28, 2008 at 1:15 PM  

@Andrei,

You are welcome. I am glad it helped.

Thanks for pointing out the typo in the last example, too! I will update the entry.

Cheers

Unknown June 4, 2008 at 7:30 AM  

I am making probes in linux with CF8 but I am getting the same problems...

any solution??

Thanks

Unknown June 6, 2008 at 2:21 AM  

Hi!!

CF 8.0.1 update fixes ffmpeg bug (it is comment in adobe update).

Before, I put 2>&1 parameter in ffmpeg calls of my sh script and it runs perfect.

I read it 20 times, but I think, this solution only works in windows.

Thanks!!!

cfSearching June 13, 2008 at 12:02 PM  

@HUGE,

Yes the 2>&1 parameters mentioned is windows o/s specific. I do not know the linux version off-hand, but I am pretty certain a similar redirection is possible. If anyone _does_ know the correct syntax, please feel free to post it!

I am very pleased to hear they fixed this issue in 8.0.1 update.

Anonymous,  November 10, 2008 at 4:21 PM  

This was incredibly helpful in general for me when using cfexecute or java.lang.runtime.exec from ColdFusion to execute programs. It's been very successful in keeping programs from staying open and tying up threads/memory.

Thanks much!

--- Derek

nath November 18, 2008 at 8:23 AM  

Thanks for putting the pieces together on this - adobe should link to this page from the cfexecute livedocs.

Cheers

nath

dcolumbus February 2, 2010 at 3:31 PM  

Okay, after tons of looking around I have decided that these are the minds to ask.

I'm trying to figure out how to create a queue with cfexecute and ffmpeg... or least figure out how to stall the executions from piling on top of each other.

I'm pulling 5 videos to be encoded from the DB at a time (that number may decrease or increase depending on server stress) and want to batch convert them so they can be moved to the FMS.

Let it be known that I am working with ffmpeg on a LINUX server, so the first thing is: what are the correct "/c" and "2>&1" for LINUX? Secondly, how to I tell ColdFusion that a video has encoded or not... or does that even matter?

I'm desperate and this post is the closest hope I have!

cfSearching February 3, 2010 at 3:20 PM  

@dcolumbus,

Does the hanging issue actually apply to linux (once the redirection issue is handled)? As far as redirection, that issue was corrected in one of the updaters. So you may want to update. For earlier versions, a quick search suggests it is very similar (if not the same) in linux.
http://en.wikipedia.org/wiki/Redirection_%28computing%29


I am not aware of any existing batch mechanism. If you want to implement your own queue, you may want to look into using cfthread. You could run the conversions in the background and within the threads run additional code upon completion or failure of the conversions. Like recording the status in your database, etcetera.
http://www.mail-archive.com/cf-talk@houseoffusion.com/msg328251.html

-Leigh

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep