[java] Weird problem reading stderr from a Process

Started by
3 comments, last by Son of Cain 16 years, 4 months ago
Howdy, long time no see! I've been working on a front end for Avs2YUV, designed to detect and mark certain scene changes in video. The video stream is sent out of Avs2YUV via the stdout stream, and my program reads the data and decides what to do with it, which is all well and good. The problem is, I can't read anything from stderr, which is where the information about the video (particularly what I need is the video's length in frames) gets sent. I know from running Avs2YUV in the console that it does send that information to stderr - I can redirect it to a file if I like, but nothing shows up at my program's end of the stream. Here's the relevant code:
import java.io.*;

public class Avs2YuvSession {
	
	//Path to Avs2YUV
	public static String AVS2YUV_PATH = ...;
	
	private Thread reader;
	private InputStream stderr;
	private OutputStreamWriter stdout;
	private Process proc;
	private Yuv4Mpeg2Stream video;
	
	public Avs2YuvSession(String scriptPath) throws IOException, Yuv4Mpeg2StreamException {
		
		//Start Avs2YUV
		proc = Runtime.getRuntime().exec(new String[] {AVS2YUV_PATH, "-slave", scriptPath, "-o", "-"});
		
		stderr = new BufferedInputStream(proc.getErrorStream());
		stdout = new OutputStreamWriter(proc.getOutputStream(), "US-ASCII");
		
		//Starts a thread that *should* print info from stderr, but never prints anything (see StderrReader class below)
		reader = new StderrReader();
		reader.start();
		
		//Wraps stdout from Avs2YUV in a YUV4MPEG stream parser
		video = new Yuv4Mpeg2Stream(new BufferedInputStream(proc.getInputStream()));
		
	}
	
	//Sends a frame number to Avs2YUV and puts the returned frame into the given buffer
	public synchronized void getFrame(int frame, I420Frame buffer) throws IOException, Yuv4Mpeg2StreamException {
		
		stdout.write(Integer.toString(frame));
		stdout.write('\n');
		stdout.flush();
		
		video.readFrame(buffer);
		
	}
	
	//Creates a buffer that can store a video frame
	public I420Frame createFrameBuffer() {
		return video.createFrameBuffer();
	}
	
	//Closes stdin and stdout
	public void close() {
		
		try { stdout.close(); } catch(Exception e) {}
		try { video.close(); } catch(Exception e) {}
		
	}
	
	//This class extends thread and just reads the stderr stream and prints its output
	private class StderrReader extends Thread {
		
		public void run() {
			
			try {
				
				//I should be getting the information from stderr printed out here, but no such luck
				
				int ch;
				
				for(;;) {
					
					//If end-of-stream was reached, clean things up and return
					if((ch = stderr.read()) < 0) {
						
						System.out.println("End of stream");
						try { stderr.close(); } catch(Exception e) {}
						return;
					
					}
					
					//Print the character read from stderr
					System.out.print((char) ch);
					
				}
				
			} catch(Exception e) {
				
				//If there was an exception reading stderr
				System.out.println("UH-OH!!! :P");
				try { stderr.close(); } catch(Exception ex) {}
				
			}
			
		}
		
	}

}
I don't think the parser (Yuv4Mpeg2Stream) is relevant, since it just operates on the given stream (stdout), but if you like I can post its code. Any idea why stderr produces output when running Avs2YUV from the console, but doesn't spit out a single char when run from a Java program with exactly the same parameters?
Advertisement
TheBluMage - this stuff is a pain. I do not know how much you have done with the Runtime.exec() method.

If you have never read through this, it is worth the time...
When Runtime.exec() won't

The code you posted is a little hard to follow since whatever calls the getFrame() method is not shown. I suspect a thread problem, but without all the code that is just a guess.

The other thing that made reading through the code confusing was this part:

stdout = new OutputStreamWriter(proc.getOutputStream(), "US-ASCII");


The reason is that proc.getOutputStream() is stdin, but you call it to stdout. So I assumed it was stdout (proc.getInputStream()), which is my falut, but it is confusing.

I would start with a simple *.exe that takes a value in and spits is out, to make sure the code is working. I have done something similar to this, and I was convinced that Java sucked, and this stuff didn't work, and turned out to be my code which had a threaded race-condition. After I fixed it, it worked every time.

Hope that helps.

I think, therefore I am. I think? - "George Carlin"
My Website: Indie Game Programming

My Twitter: https://twitter.com/indieprogram

My Book: http://amzn.com/1305076532

Yeah, I know the name is wrong, but I was torn between naming an OutputStream stdin and using the proper term. That, and I'm a tad dyslexic. :P The getFrame method works fine - I don't know if I made that clear or not, but I can get video frames just fine, since they come from stdout. I also tried reading from stderr in the same thread (just using reader.run() instead of reader.start()) but still got nothing. Later this evening I'll write up a quick app in C to spit some data out of stdout and stderr and see if I can get my front-end to read it.
This is really weird. I created a sorta dummy program that just sends blank frames to stdout and prints some information to stderr. It works fine in my front-end and I can read the info from stderr. So, I went back to Avs2YUV. When I run it from the console, redirecting nothing, I always see the information from stderr and stdout. However, when I redirect stderr to a file, sometimes it works and sometimes I get a blank file.

It seems that the current directory has something to do with it. If the current directory is on the C: drive and I redirect stderr to a file on the C: drive, it works. Likewise, if the current directory is on the G: drive and I redirect stderr to a file on the G: drive, it works. But, if the current directory is on the G: drive and I redirect stderr to a file on the C: drive, or vice versa, I get a blank file! My dummy program doesn't exhibit the same behavior. (It works no matter what.)

I'm lost, and this doesn't really seem like a Java problem anymore; I don't know what kind of problem this is.
Java problem or not, a little tip: try using the ProcessBuilder. Seems to be a better hammer for this nail :p
a.k.a javabeats at yahoo.ca

This topic is closed to new replies.

Advertisement