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 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!

9 comments:

Ben Forta February 28, 2008 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 8:36 AM  

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

Andrei,  March 28, 2008 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 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

HUGE June 4, 2008 7:30 AM  

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

any solution??

Thanks

HUGE June 6, 2008 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 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.

Derek Bredensteiner November 10, 2008 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 8:23 AM  

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

Cheers

nath

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep