Playing audio in VOB files

89 views
Skip to first unread message

LeoKh

unread,
Feb 19, 2009, 7:23:17 PM2/19/09
to xuggler-users
Hi Art,
one more problem.
While trying to play VOB file (from DVD) om my PC via
DecodeAndPlayAudioAndVideo got an exception:

Exception in thread "main" java.lang.IllegalArgumentException: No line
matching interface SourceDataLine supporting format PCM_SIGNED 48000.0
Hz, 16 bit, 6 channels, 12 bytes/frame, little-endian is supported.
at javax.sound.sampled.AudioSystem.getLine(AudioSystem.java:459)
at com.xuggle.xuggler.demos.DecodeAndPlayAudioAndVideo.openJavaSound
(DecodeAndPlayAudioAndVideo.java:373)
at com.xuggle.xuggler.demos.DecodeAndPlayAudioAndVideo.main
(DecodeAndPlayAudioAndVideo.java:160)

DecodeAndPlayVideo for this file is OK.
Playing with ffmpeg is also OK.

Looks like I need to modify some params of DataLine.Info.
Can you give any clue?

Regards,
LK

Art Clarke

unread,
Feb 19, 2009, 7:27:01 PM2/19/09
to xuggle...@googlegroups.com
Looks like your sound card can't play 48khz audio.  You can use the IAudioResampler interface to resample your audio to something more standard (e.g. 44.1khz), and then play it back in a format your machine does support.

My guess is ffplay (I assume that's what you meant by ffmpeg) is doing that resample for you.  If you'd like to see examples of the IAudioResample in action, check out the com.xuggle.xuggler.Converter.java sample code.

Hope that helps,

- Art
--
http://www.xuggle.com/
xu‧ggle (zŭ' gl) v. To freely encode, decode, and experience audio and video.

Use Xuggle to get the power of FFMPEG in Java.

Leonid Khodulev

unread,
Feb 27, 2009, 2:37:56 PM2/27/09
to xuggle...@googlegroups.com
Well,
I'd tried to run:
com.xuggle.xuggler.Converter -acodec libmp3lame -asamplerate 44100 -abitrate 128000 -achannels 2 testMedia\13_Going_on_30_Trailer.vob testResults\13_Going_on_30_Trailer_11.flv
Result is :
Error: could not open audio resampler for stream: 1 (Stream 1 is audio)
Looks like I'd missed something else...
Regards,
LK

Art Clarke

unread,
Feb 27, 2009, 2:40:26 PM2/27/09
to xuggle...@googlegroups.com
Can you post your code to something like pastebin and I'll take a look.  That error means you didn't set up the resampler correctly.

- Art

LeoKh

unread,
Feb 27, 2009, 2:53:15 PM2/27/09
to xuggler-users
Here it is:

/*
* Copyright (c) 2008-2009 by Xuggle Inc. All rights reserved.
*
* It is REQUESTED BUT NOT REQUIRED if you use this library, that you
let
* us know by sending e-mail to in...@xuggle.com telling us briefly how
you're
* using the library and what you like or don't like about it.
*
* This library is free software; you can redistribute it and/or
modify it under the
* terms of the GNU Lesser General Public License as published by the
Free Software
* Foundation; either version 2.1 of the License, or (at your option)
any later
* version.
*
* This library is distributed in the hope that it will be useful, but
WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details.
*
* You should have received a copy of the GNU Lesser General Public
License along
* with this library; if not, write to the Free Software Foundation,
Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.xuggle.xuggler;

import org.apache.commons.cli.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xuggle.xuggler.IAudioResampler;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IContainerFormat;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IRational;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoResampler;

import com.xuggle.xuggler.io.URLProtocolManager;

/**
* An example class that shows how to use the Xuggler library to open,
decode, re-sample, encode and write media files.
* <p>
* This method is called by the {@link Xuggler} class to do all the
heavy lifting.
* </p><p>
* Read <a href="{@docRoot}/src-html/com/xuggle/xuggler/
Converter.html">the Converter.java source</a>
* for a good example of a program exercising this library.
* </p>
*
* @author aclarke
*
*/
public class Converter
{

/*
usage: Xuggler [options] input_url output_url
-abitrate <abit-rate> bit rate to encode audio
with (in bps) (e.g. "60000")
-achannels <channels> number of audio channels (1
or 2) to encode with (e.g. "2")
-acodec <codec> audio codec to encode with
(e.g. "libmp3lame")
-aquality <quality> quality setting to use for
audio. 0 means same as source; higher numbers are
(perversely) lower quality.
Defaults to 0.
-asamplerate <sample-rate> audio sample rate to (up/
down) encode with (in hz) (e.g. "22050")
-containerformat <container-format> output container format to
use (e.g. "mov")
-help print this message
-vbitrate <vbitrate> bit rate to encode video
with (in bps) (e.g. "60000")
-vbitratetolerance <vbitratetolerance> bit rate tolerance the
bitstream is allowed to diverge from the
reference (in bits) (e.g.
"1200000")
-vcodec <codec> video codec to encode with
(e.g. "mpeg4")
-vquality <quality> quality setting to use for
video. 0 means same as source; higher numbers
are (perversely) lower
quality. Defaults to 0.
-vscalefactor <factor> scaling factor to scale
output video by (e.g. "0.75")
*/

static {
// this forces the FFMPEG io library to be loaded which means we
can bypass FFMPEG's file io if needed
URLProtocolManager.getManager();
}

private final Logger log = LoggerFactory.getLogger(this.getClass());

/**
* A container we'll use to read data from.
*/
private IContainer mIContainer = null;
/**
* A container we'll use to write data from.
*/
private IContainer mOContainer = null;

/**
* A set of {@link IStream} values for each stream in the input
{@link IContainer}.
*/
private IStream[] mIStreams = null;
/**
* A set of {@link IStreamCoder} objects we'll use to decode audio
and video.
*/
private IStreamCoder[] mICoders = null;

/**
* A set of {@link IStream} objects for each stream we'll output to
the output {@link IContainer}.
*/
private IStream[] mOStreams = null;
/**
* A set of {@link IStreamCoder} objects we'll use to encode audio
and video.
*/
private IStreamCoder[] mOCoders = null;

/**
* A set of {@link IVideoPicture} objects that we'll use to hold
decoded video data.
*/
private IVideoPicture[] mIVideoPictures = null;
/**
* A set of {@link IVideoPicture} objects we'll use to hold
potentially-resampled video data before we encode it.
*/
private IVideoPicture[] mOVideoPictures = null;

/**
* A set of {@link IAudioSamples} objects we'll use to hold decoded
audio data.
*/
private IAudioSamples[] mISamples = null;
/**
* A set of {@link IAudioSamples} objects we'll use to hold
potentially-resampled audio data before we encode it.
*/
private IAudioSamples[] mOSamples = null;

/**
* A set of {@link IAudioResampler} objects (one for each stream)
we'll use to resample audio if needed.
*/
private IAudioResampler[] mASamplers = null;
/**
* A set of {@link IVideoResampler} objects (one for each stream)
we'll use to resample video if needed.
*/
private IVideoResampler[] mVSamplers = null;

/**
*
* A simple test of xuggler, this program takes an input
* file, and outputs it as an output file.
*
* @param args The command line args passed to this program.
*/
public static void main(String[] args)
{
Converter converter = new Converter();

try {
// first define options
Options options = converter.defineOptions();
// And then parse them.
CommandLine cmdLine = converter.parseOptions(options, args);
// Finally, run the converter.
converter.run(cmdLine);
}
catch (Exception exception)
{
System.err.printf("Error: %s\n", exception.getMessage());
}


}

/**
* Should we convert audio
*/
private boolean mHasAudio = true;
/**
* Should we convert video
*/
private boolean mHasVideo = true;

/**
* Should we force an interleaving of the output
*/
private final boolean mForceInterleave = true;

/**
* Define all the command line options this program can take.
* @return The set of accepted options.
*/
public Options defineOptions()
{
Options options = new Options();

Option help = new Option( "help", "print this message");

/*
* Output container options.
*/
OptionBuilder.withArgName("container-format");
OptionBuilder.hasArg(true);
OptionBuilder.withDescription("output container format to use
(e.g. \"mov\")");
Option containerFormat = OptionBuilder.create("containerformat");

/*
* Audio options
*/
OptionBuilder.hasArg(false);
OptionBuilder.withDescription("no audio");
Option ano = OptionBuilder.create("ano");

OptionBuilder.withArgName("codec");
OptionBuilder.hasArg(true);
OptionBuilder.withDescription("audio codec to encode with (e.g.
\"libmp3lame\")");
Option acodec = OptionBuilder.create("acodec");

OptionBuilder.withArgName("sample-rate");
OptionBuilder.hasArg(true);
OptionBuilder.withDescription("audio sample rate to (up/down)
encode with (in hz) (e.g. \"22050\")");
Option asamplerate = OptionBuilder.create("asamplerate");

OptionBuilder.withArgName("channels");
OptionBuilder.hasArg(true);
OptionBuilder.withDescription("number of audio channels (1 or 2)
to encode with (e.g. \"2\")");
Option achannels = OptionBuilder.create("achannels");

OptionBuilder.withArgName("abit-rate");
OptionBuilder.hasArg(true);
OptionBuilder.withDescription("bit rate to encode audio with (in
bps) (e.g. \"60000\")");
Option abitrate= OptionBuilder.create("abitrate");

OptionBuilder.withArgName("quality");
OptionBuilder.hasArg(true);
OptionBuilder.withDescription("quality setting to use for audio.
0 means same as source; higher numbers are (perversely) lower
quality. Defaults to 0.");
Option aquality= OptionBuilder.create("aquality");

/*
* Video options
*/
OptionBuilder.hasArg(false);
OptionBuilder.withDescription("no video");
Option vno = OptionBuilder.create("vno");

OptionBuilder.withArgName("codec");
OptionBuilder.hasArg(true);
OptionBuilder.withDescription("video codec to encode with (e.g.
\"mpeg4\")");
Option vcodec = OptionBuilder.create("vcodec");

OptionBuilder.withArgName("factor");
OptionBuilder.hasArg(true);
OptionBuilder.withDescription("scaling factor to scale output
video by (e.g. \"0.75\")");
Option vscaleFactor = OptionBuilder.create("vscalefactor");

OptionBuilder.withArgName("vbitrate");
OptionBuilder.hasArg(true);
OptionBuilder.withDescription("bit rate to encode video with (in
bps) (e.g. \"60000\")");
Option vbitrate= OptionBuilder.create("vbitrate");

OptionBuilder.withArgName("vbitratetolerance");
OptionBuilder.hasArg(true);
OptionBuilder.withDescription("bit rate tolerance the bitstream is
allowed to diverge from the reference (in bits) (e.g. \"1200000\")");
Option vbitratetolerance= OptionBuilder.create
("vbitratetolerance");


OptionBuilder.withArgName("quality");
OptionBuilder.hasArg(true);
OptionBuilder.withDescription("quality setting to use for video.
0 means same as source; higher numbers are (perversely) lower
quality. Defaults to 0.");
Option vquality= OptionBuilder.create("vquality");

options.addOption(help);
options.addOption(containerFormat);
options.addOption(ano);
options.addOption(acodec);
options.addOption(asamplerate);
options.addOption(achannels);
options.addOption(abitrate);
options.addOption(aquality);
options.addOption(vno);
options.addOption(vcodec);
options.addOption(vscaleFactor);
options.addOption(vbitrate);
options.addOption(vbitratetolerance);
options.addOption(vquality);

return options;
}

/**
* Given a set of arguments passed into this object, return back a
parsed command line.
* @param opt A set of options as defined by {@link #defineOptions
()}.
* @param args A set of command line arguments passed into this
class.
* @return A parsed command line.
* @throws ParseException If there is an error in the command line.
*/
public CommandLine parseOptions(Options opt, String[] args)
throws ParseException
{
CommandLine cmdLine = null;

CommandLineParser parser = new GnuParser();

cmdLine = parser.parse(opt,args);

if (cmdLine.hasOption("help"))
{
HelpFormatter help = new HelpFormatter();
help.printHelp("Xuggler [options] input_url output_url", opt);
System.exit(1);
}
// Make sure we have only two left over args
if (cmdLine.getArgs().length != 2)
throw new ParseException("missing input or output url");

return cmdLine;
}

/**
* Get an integer value from a command line argument.
* @param cmdLine A parsed command line (as returned from {@link
#parseOptions(Options, String[])}
* @param key The key for an option you want.
* @param defaultVal The default value you want set if the key is
not present in cmdLine.
* @return The value for the key in the cmdLine, or defaultVal if
it's not there.
*/
private int getIntOptionValue(CommandLine cmdLine, String key,
int defaultVal)
{
int retval = defaultVal;
String optValue = cmdLine.getOptionValue(key);

if (optValue != null)
{
try {
retval = Integer.parseInt(optValue);
} catch (Exception ex)
{
log.warn(
"Option \"{}\" value \"{}\" cannot be converted to
integer; using {} instead",
new Object[] { key, optValue, defaultVal });
}
}
return retval;
}

/**
* Get a double value from a command line argument.
* @param cmdLine A parsed command line (as returned from {@link
#parseOptions(Options, String[])}
* @param key The key for an option you want.
* @param defaultVal The default value you want set if the key is
not present in cmdLine.
* @return The value for the key in the cmdLine, or defaultVal if
it's not there.
*/
private double getDoubleOptionValue(CommandLine cmdLine, String key,
double defaultVal)
{
double retval = defaultVal;
String optValue = cmdLine.getOptionValue(key);

if (optValue != null)
{
try {
retval = Double.parseDouble(optValue);
} catch (Exception ex)
{
log.warn(
"Option \"{}\" value \"{}\" cannot be converted to double;
using {} instead",
new Object[] { key, optValue, defaultVal });
}
}
return retval;
}

/**
* Open an initialize all Xuggler objects needed to encode and
decode a video file.
* @param cmdLine A command line (as returned from {@link
#parseOptions(Options, String[])}) that
* specifies what files we want to process and how to process
them.
* @return Number of streams in the input file, or <= 0 on error.
*/

int setupStreams(CommandLine cmdLine)
{
String inputURL = cmdLine.getArgs()[0];
String outputURL = cmdLine.getArgs()[1];

mHasAudio = !cmdLine.hasOption("ano");
mHasVideo = !cmdLine.hasOption("vno");

String acodec = cmdLine.getOptionValue("acodec");
String vcodec = cmdLine.getOptionValue("vcodec");
String containerFormat = cmdLine.getOptionValue
("containerformat");
int aquality = getIntOptionValue(cmdLine, "aquality", 0);

int sampleRate = getIntOptionValue(cmdLine, "asamplerate", 0);
int channels = getIntOptionValue(cmdLine, "achannels", 0);
int abitrate = getIntOptionValue(cmdLine, "abitrate", 0);
int vbitrate = getIntOptionValue(cmdLine, "vbitrate", 0);
int vbitratetolerance = getIntOptionValue(cmdLine,
"vbitratetolerance", 0);
int vquality = getIntOptionValue(cmdLine, "vquality", 0);
double vscaleFactor = getDoubleOptionValue(cmdLine,
"vscalefactor", 1.0);

// Should have everything now!
int retval = 0;

/**
* Create one container for input, and one for output.
*/
mIContainer = IContainer.make();
mOContainer = IContainer.make();
IContainerFormat oFmt = null;

/**
* Open the input container for Reading.
*/
retval = mIContainer.open(inputURL, IContainer.Type.READ, null);
if (retval < 0)
throw new RuntimeException("could not open url: " + inputURL);

/**
* If the user EXPLICITLY asked for a output container format,
we'll try to
* honor their request here.
*/
if (containerFormat != null)
{
oFmt = IContainerFormat.make();
/**
* Try to find an output format based on what the user
specified, or failing
* that, based on the outputURL (e.g. if it ends in .flv, we'll
guess FLV).
*/
retval = oFmt.setOutputFormat(containerFormat, outputURL, null);
if (retval < 0)
throw new RuntimeException("could not find output container
format: " + containerFormat);
}
/**
* Open the output container for writing. If oFmt is null, we are
telling
* Xuggler to guess the output container format based on the
outputURL.
*/
retval = mOContainer.open(outputURL, IContainer.Type.WRITE, oFmt);
if (retval < 0)
throw new RuntimeException("could not open output url: " +
outputURL);

/**
* Find out how many streams are there in the input container?
For example,
* most FLV files will have 2 -- 1 audio stream and 1 video
stream.
*/
int numStreams = mIContainer.getNumStreams();
if (numStreams <= 0)
throw new RuntimeException("not streams in input url: " +
inputURL);

/**
* Here we create IStream, IStreamCoders and other objects for
each
* input stream.
*
* We make parallel objects for each output stream as well.
*/
mIStreams = new IStream[numStreams];
mICoders = new IStreamCoder[numStreams];
mOStreams = new IStream[numStreams];
mOCoders = new IStreamCoder[numStreams];
mASamplers = new IAudioResampler[numStreams];
mVSamplers = new IVideoResampler[numStreams];
mIVideoPictures = new IVideoPicture[numStreams];
mOVideoPictures = new IVideoPicture[numStreams];
mISamples = new IAudioSamples[numStreams];
mOSamples = new IAudioSamples[numStreams];

/**
* Now let's go through the input streams one by one and
* explicitly set up our contexts.
*/
for (int i = 0; i < numStreams; i++)
{
/**
* Get the IStream for this input stream.
*/
IStream is = mIContainer.getStream(i);
/**
* And get the input stream coder. Xuggler will set up all
sorts
* of defaults on this StreamCoder for you (such as the audio
sample rate)
* when you open it.
*
* You can create IStreamCoders yourself using IStreamCoder#make
(IStreamCoder.Direction),
* but then you have to set all parameters yourself.
*/
IStreamCoder ic = is.getStreamCoder();

/**
* Find out what Codec Xuggler guessed the input stream was
encoded with.
*/
ICodec.Type cType = ic.getCodecType();

mIStreams[i] = is;
mICoders[i] = ic;
mOStreams[i] = null;
mOCoders[i] = null;
mASamplers[i] = null;
mVSamplers[i] = null;
mIVideoPictures[i] = null;
mOVideoPictures[i] = null;
mISamples[i] = null;
mOSamples[i] = null;

if (cType == ICodec.Type.CODEC_TYPE_AUDIO && mHasAudio)
{
/**
* So it looks like this stream as an audio stream. Now we
add
* an audio stream to the output container that we will use
* to encode our resampled audio.
*/
IStream os=mOContainer.addNewStream(i);

/**
* And we ask the IStream for an appropriately configured
IStreamCoder for output.
*
* Unfortunately you still need to specify a lot of things for
outputting
* (because we can't really guess what you want to encode as).
*/
IStreamCoder oc = os.getStreamCoder();

mOStreams[i] = os;
mOCoders[i] = oc;

/**
* First, did the user specify an audio codec?
*/
if (acodec != null)
{
ICodec codec = null;
/**
* Looks like they did specify one; let's look it up by
name.
*/
codec = ICodec.findEncodingCodecByName(acodec);
if (codec == null || codec.getType() != cType)
throw new RuntimeException("could not find encoder: " +
acodec);
/**
* Now, tell the output stream coder that it's to use that
codec.
*/
oc.setCodec(codec);
} else
{
/**
* Looks like the user didn't specify an output coder for
audio.
*
* So we ask Xuggler to guess an appropriate output coded
based
* on the URL, container format, and that it's audio.
*/
ICodec codec = ICodec.guessEncodingCodec(oFmt, null,
outputURL, null, cType);
if (codec == null)
throw new RuntimeException("could not guess "+cType+
" encoder for: " + outputURL);
/**
* Now let's use that.
*/
oc.setCodec(codec);
}

/**
* In general a IStreamCoder encoding audio needs to know:
* 1) A ICodec to use.
* 2) The sample rate and number of channels of the audio.
* Most everything else can be defaulted.
*/

/**
* If the user didn't specify a sample rate to encode as,
* then just use the same sample rate as the input.
*/
if (sampleRate == 0)
sampleRate = ic.getSampleRate();
oc.setSampleRate(sampleRate);
/**
* If the user didn't specify a bit rate to encode as,
* then just use the same bit as the input.
*/
if (abitrate == 0)
abitrate = ic.getBitRate();
oc.setBitRate(abitrate);
/**
* If the user didn't specify the number of channels to encode
* audio as, just assume we're keeping the same number of
channels.
*/
if (channels == 0)
channels = ic.getChannels();
oc.setChannels(channels);

/**
* And set the quality (which defaults to 0, or highest, if
the user doesn't tell us one).
*/
oc.setGlobalQuality(aquality);

/**
* Now check if our output channels or sample rate differ
* from our input channels or sample rate.
*
* If they do, we're going to need to resample the input audio
* to be in the right format to output.
*/
if (oc.getChannels() != ic.getChannels() ||
oc.getSampleRate() != ic.getSampleRate())
{
/**
* Create an audio resampler to do that job.
*/
mASamplers[i] = IAudioResampler.make(
oc.getChannels(), ic.getChannels(),
oc.getSampleRate(), ic.getSampleRate());
if (mASamplers[i] == null)
{
throw new RuntimeException("could not open audio resampler
for stream: " + i);
}
} else {
mASamplers[i] = null;
}
/**
* Finally, create some buffers for the input and output audio
themselves.
*
* We'll use these repeated during the #run(CommandLine)
method.
*/
mISamples[i] = IAudioSamples.make(1024, ic.getChannels());
mOSamples[i] = IAudioSamples.make(1024, oc.getChannels());
}
else if (cType == ICodec.Type.CODEC_TYPE_VIDEO && mHasVideo)
{
/**
* If you're reading these commends, this does the same thing
as the above
* branch, only for video. I'm going to assume you read those
commends and
* will only document something substantially different here.
*/
IStream os=mOContainer.addNewStream(i);
IStreamCoder oc = os.getStreamCoder();

mOStreams[i] = os;
mOCoders[i] = oc;

if (vcodec != null)
{
ICodec codec = null;
codec = ICodec.findEncodingCodecByName(vcodec);
if (codec == null || codec.getType() != cType)
throw new RuntimeException("could not find encoder: " +
vcodec);
oc.setCodec(codec);
oc.setGlobalQuality(0);
} else
{
ICodec codec = ICodec.guessEncodingCodec(oFmt, null,
outputURL, null, cType);
if (codec == null)
throw new RuntimeException("could not guess "+cType+
" encoder for: " + outputURL);

oc.setCodec(codec);
}

/**
* In general a IStreamCoder encoding video needs to know:
* 1) A ICodec to use.
* 2) The Width and Height of the Video
* 3) The pixel format (e.g. IPixelFormat.Type#YUV420P) of
the video data.
* Most everything else can be defaulted.
*/
if (vbitrate == 0)
vbitrate = ic.getBitRate();
oc.setBitRate(vbitrate);
if (vbitratetolerance > 0)
oc.setBitRateTolerance(vbitratetolerance);

int oWidth = ic.getWidth();
int oHeight = ic.getHeight();

if (oHeight <= 0 || oWidth <= 0)
throw new RuntimeException("could not find width or height
in url: " + inputURL);

/**
* For this program we don't allow the user to specify the
pixel format type;
* we force the output to be the same as the input.
*/
oc.setPixelType(ic.getPixelType());

if (vscaleFactor != 1.0)
{
/**
* In this case, it looks like the output video requires
rescaling,
* so we create a IVideoResampler to do that dirty work.
*/
oWidth = (int)(oWidth * vscaleFactor);
oHeight = (int)(oHeight * vscaleFactor);

mVSamplers[i] = IVideoResampler.make(
oWidth, oHeight, oc.getPixelType(),
ic.getWidth(), ic.getHeight(), ic.getPixelType());
if (mVSamplers[i] == null)
{
throw new RuntimeException("This version of Xuggler does
not support video resampling " + i);
}
} else {
mVSamplers[i] = null;
}
oc.setHeight(oHeight);
oc.setWidth(oWidth);

oc.setFlag(IStreamCoder.Flags.FLAG_QSCALE, true);
oc.setGlobalQuality(vquality);

/**
* TimeBases are important, especially for Video. In general
Audio encoders
* will assume that any new audio happens IMMEDIATELY after
any prior
* audio finishes playing. But for video, we need to make
sure it's
* being output at the right rate.
*
* In this case we make sure we set the same time base as the
input,
* and then we don't change the time stamps of any
IVideoPictures.
*
* But take my word that time stamps are tricky, and this
only touches
* the envelope. The good news is, it's easier in Xuggler
than some
* other systems.
*/
IRational num = null;
num = ic.getFrameRate();
oc.setFrameRate(num);
oc.setTimeBase(IRational.make(num.getDenominator(),
num.getNumerator()));
num = null;

/**
* And allocate buffers for us to store decoded and resample
video pictures.
*/
mIVideoPictures[i] = IVideoPicture.make(ic.getPixelType(),
ic.getWidth(),
ic.getHeight());
mOVideoPictures[i] = IVideoPicture.make(oc.getPixelType(),
oc.getWidth(),
oc.getHeight());
}
else
{
log.warn("Ignoring input stream {} of type {}", i, cType);
}

/**
* Now, once you've set up all the parameters on the
StreamCoder, you must
* open() them so they can do work.
*
* They will return an error if not configured correctly, so we
check for
* that here.
*/
if (mOCoders[i] != null)
{
retval = mOCoders[i].open();
if (retval < 0)
throw new RuntimeException("could not open output encoder
for stream: " + i);
retval = mICoders[i].open();
if (retval < 0)
throw new RuntimeException("could not open input decoder for
stream: " + i);
}
}

/**
* Pretty much every output container format has a header they
need written, so
* we do that here.
*
* You must configure your output IStreams correctly before
writing a header,
* and few formats deal nicely with key parameters changing (e.g.
video width)
* after a header is written.
*/
retval = mOContainer.writeHeader();
if (retval < 0)
throw new RuntimeException("Could not write header for: " +
outputURL);

/**
* That's it with setup; we're good to begin!
*/
return numStreams;
}

/**
* Close and release all resources we used to run this program.
*/
void closeStreams()
{
int numStreams = 0;
int i = 0;

numStreams = mIContainer.getNumStreams();
/**
* Some video coders (e.g. MP3) will often "read-ahead" in a
stream and keep
* extra data around to get efficient compression. But they need
some way to know
* they're never going to get more data. The convention for that
case is to pass
* null for the IMediaData (e.g. IAudioSamples or IVideoPicture)
in encodeAudio(...)
* or encodeVideo(...) once before closing the coder.
*
* In that case, the IStreamCoder will flush all data.
*/
for (i = 0; i < numStreams; i++)
{
if (mOCoders[i] != null) {
IPacket oPacket = IPacket.make();
if (mOCoders[i].getCodecType() ==
ICodec.Type.CODEC_TYPE_AUDIO)
mOCoders[i].encodeAudio(oPacket, null, 0);
else
mOCoders[i].encodeVideo(oPacket, null, 0);
if (oPacket.isComplete())
mOContainer.writePacket(oPacket, mForceInterleave);
}
}
/**
* Some container formats require a trailer to be written to avoid
a corrupt files.
*
* Others, such as the FLV container muxer, will take a
writeTrailer() call to tell
* it to seek() back to the start of the output file and write the
(now known) duration
* into the Meta Data.
*
* So trailers are required. In general if a format is a
streaming format, then the
* writeTrailer() will never seek backwards.
*
* Make sure you don't close your codecs before you write your
trailer, or
* we'll complain loudly and not actually write a trailer.
*/
int retval = mOContainer.writeTrailer();
if (retval < 0)
throw new RuntimeException("Could not write trailer to output
file");

/**
* We do a nice clean-up here to show you how you should do it.
*
* That said, Xuggler goes to great pains to clean up after you if
you
* forget to release things. But still, you should be a good boy
or giral and
* clean up yourself.
*/
for (i = 0; i < numStreams; i++)
{
if (mOCoders[i] != null) {
/**
* And close the input coder to tell Xuggler it can release
all native memory.
*/
mOCoders[i].close();
}
mOCoders[i] = null;
if (mICoders[i] != null)
/**
* Close the input coder to tell Xuggler it can release all
native memory.
*/
mICoders[i].close();
mICoders[i] = null;
}

/**
* Tell Xuggler it can close the output file, write all data, and
free all relevant memory.
*/
mOContainer.close();
/**
* And do the same with the input file.
*/
mIContainer.close();

/**
* Technically setting everything to null here doesn't do anything
but tell
* Java it can collect the memory it used.
*
* The interesting thing to note here is that if you forget to
close()
* a Xuggler object, but also loose all references to it from
Java, you
* won't leak the native memory. Instead, we'll clean up after
you,
* but we'll complain LOUDLY in your logs, so you really don't
want to do that.
*/
mOContainer = null;
mIContainer = null;
mISamples = null;
mOSamples = null;
mIVideoPictures = null;
mOVideoPictures = null;
mOCoders = null;
mICoders = null;
mASamplers = null;
mVSamplers = null;
}

/** Allow child class to override this method to alter the audio
frame
* before it is rencoded and written. In this implementation the
* audio frame is passed through unmodified.
*
* @param audioFrame the source audio frame to be modified
*
* @return the modified audio frame
*/

protected IAudioSamples alterAudioFrame(IAudioSamples audioFrame)
{
return audioFrame;
}

/** Allow child class to override this method to alter the video
frame
* before it is rencoded and written. In this implementation the
* video frame is passed through unmodified.
*
* @param videoFrame the source video frame to be modified
*
* @return the modified video frame
*/

protected IVideoPicture alterVideoFrame(IVideoPicture videoFrame)
{
return videoFrame;
}

/**
* Takes a given command line and decodes the input file, and
encodes with
* new parameters to the output file.
* @param cmdLine A command line returned from {@link #parseOptions
(Options, String[])}.
*/
public void run(CommandLine cmdLine)
{
/**
* Setup all our input and outputs
*/
setupStreams(cmdLine);

/**
* Create packet buffers for reading data from and writing data to
the conatiners.
*/
IPacket iPacket = IPacket.make();
IPacket oPacket = IPacket.make();

/**
* Keep some "pointers' we'll use for the audio we're working
with.
*/
IAudioSamples inSamples = null;
IAudioSamples outSamples = null;
IAudioSamples reSamples = null;

int retval = 0;

/**
* And keep some convenience pointers for the specific stream
we're working on
* for a packet.
*/
IStreamCoder ic = null;
IStreamCoder oc = null;
IAudioResampler as = null;
IVideoResampler vs = null;
IVideoPicture inFrame = null;
IVideoPicture reFrame = null;

/**
* Now, we've already opened the files in #setupStreams
(CommandLine). We
* just keep reading packets from it until the IContainer returns
<0
*/
while (mIContainer.readNextPacket(iPacket) == 0)
{
/**
* Find out which stream this packet belongs to.
*/
int i = iPacket.getStreamIndex();
int offset = 0;

/**
* And look up the appropriate objects that are working on that
stream.
*/
ic = mICoders[i];
oc = mOCoders[i];
as = mASamplers[i];
vs = mVSamplers[i];
inFrame = mIVideoPictures[i];
reFrame = mOVideoPictures[i];
inSamples = mISamples[i];
reSamples = mOSamples[i];

/**
* Find out if the stream is audio or video.
*/
ICodec.Type cType = ic.getCodecType();

if (cType == ICodec.Type.CODEC_TYPE_AUDIO && mHasAudio)
{
/**
* Decoding audio works by taking the data in the packet, and
eating
* chunks from it to create decoded raw data.
*
* However, there may be more data in a packet than is needed
* to get one set of samples (or less), so you need to iterate
* through the byts to get that data.
*
* The following loop is the standard way of doing that.
*/
while (offset < iPacket.getSize())
{
retval = ic.decodeAudio(inSamples, iPacket, offset);
if (retval <= 0)
throw new RuntimeException("could not decode audio");

/**
* If not an error, the decodeAudio returns the number of
bytes
* it consumed. We use that so the next time around the
loop we
* get new data.
*/
offset += retval;
int numSamplesConsumed = 0;
/**
* If as is not null then we know a resample was needed, so
we do that resample now.
*/
if (as != null && inSamples.getNumSamples() >0)
{
retval = as.resample(reSamples, inSamples,
inSamples.getNumSamples());

outSamples = reSamples;
}
else
{
outSamples = inSamples;
}

/**
* Include call a hook to derivied classes to allow them to
* alter the audio frame.
*/

outSamples = alterAudioFrame(outSamples);

/**
* Now that we've resampled, it's time to encode the audio.
*
* This workflow is similar to decoding; you may have more,
less or just enough
* audio samples available to encode a packet. But you must
iterate through.
*
* Unfortunately (don't ask why) there is a slight
difference between
* encodeAudio and decodeAudio; encodeAudio returns the
number of samples consumed,
* NOT the number of bytes. This can be confusing, and we
encourage you to read
* the IAudioSamples documentation to find out what the
difference is.
*
* But in any case, the following loop encodes the samples
we have into packets.
*/
while (numSamplesConsumed < outSamples.getNumSamples()) {
retval = oc.encodeAudio(oPacket, outSamples,
numSamplesConsumed);
if (retval <= 0)
throw new RuntimeException("Could not encode any audio:
" + retval);
/**
* Increment the number of samples consumed, so that the
next time through this
* loop we encode new audio
*/
numSamplesConsumed += retval;
if (oPacket.isComplete())
{
/**
* If we got a complete packet out of the encoder, then
go ahead and write it
* to the container.
*/
retval = mOContainer.writePacket(oPacket,
mForceInterleave);
if (retval < 0)
throw new RuntimeException("could not write output
packet");
}
}
}

}
else if (cType == ICodec.Type.CODEC_TYPE_VIDEO && mHasVideo)
{
/**
* This encoding workflow is pretty much the same as the for
the audio above.
*
* The only major delta is that encodeVideo() will always
consume one frame (whereas
* encodeAudio() might only consume some samples in an
IAudioSamples buffer); it might
* not be able to output a packet yet, but you can assume that
you it consumes the entire frame.
*/
IVideoPicture outFrame = null;
while (offset < iPacket.getSize()) {
retval = ic.decodeVideo(inFrame, iPacket, offset);
if (retval <= 0)
throw new RuntimeException("could not decode any video");
offset += retval;
if (inFrame.isComplete())
{
if (vs != null)
{
retval = vs.resample(reFrame, inFrame);
if (retval < 0)
throw new RuntimeException("could not resample
video");
outFrame = reFrame;
}
else
{
outFrame = inFrame;
}

/**
* Include call a hook to derivied classes to allow them
to
* alter the audio frame.
*/

outFrame = alterVideoFrame(outFrame);

outFrame.setQuality(0);
retval = oc.encodeVideo(oPacket, outFrame, 0);
if (retval < 0)
throw new RuntimeException("could not encode video");
if (oPacket.isComplete())
{
retval = mOContainer.writePacket(oPacket,
mForceInterleave);
if (retval < 0)
throw new RuntimeException("could not write video
packet");
}
}
}
}
else
{
/**
* Just to be complete; there are other types of data that can
show up
* in streams (e.g. SUB TITLE).
*
* Right now we don't support decoding and encoding that data,
but youc
* could still decide to write out the packets if you wanted.
*/
log.trace("ignoring packet of type: {}", cType);
}

}

// and cleanup.
closeStreams();
}
}


On Feb 27, 11:40 am, Art Clarke <acla...@xuggle.com> wrote:
> Can you post your code to something like pastebin and I'll take a look.
> That error means you didn't set up the resampler correctly.
>
> - Art
>
>
>
> On Fri, Feb 27, 2009 at 11:37 AM, Leonid Khodulev <lk31...@gmail.com> wrote:
> > Well,
> > I'd tried to run:
> > com.xuggle.xuggler.Converter -acodec libmp3lame -asamplerate 44100
> > -abitrate 128000 -achannels 2 testMedia\13_Going_on_30_Trailer.vob
> > testResults\13_Going_on_30_Trailer_11.flv
> > Result is :
> > Error: could not open audio resampler for stream: 1 (Stream 1 is audio)
> > Looks like I'd missed something else...
> > Regards,
> > LK
>
> > On Thu, Feb 19, 2009 at 4:27 PM, Art Clarke <acla...@xuggle.com> wrote:
>
> >> Looks like your sound card can't play 48khz audio.  You can use the
> >> IAudioResampler interface to resample your audio to something more standard
> >> (e.g. 44.1khz), and then play it back in a format your machine does support.
>
> >> My guess is ffplay (I assume that's what you meant by ffmpeg) is doing
> >> that resample for you.  If you'd like to see examples of the IAudioResample
> >> in action, check out the com.xuggle.xuggler.Converter.java sample code.
>
> >> Hope that helps,
>
> >> - Art
>
> --http://www.xuggle.com/

Art Clarke

unread,
Feb 27, 2009, 2:57:30 PM2/27/09
to xuggle...@googlegroups.com
Oh, you're just using our sample program.  Gotcha (please use pastebin.com in the future; it seriously reduces the size of the message and is much easier to parse).

Can you post the audio file that's failing online somewhere so I can download and take a look?

- Art

Leonid Khodulev

unread,
Feb 27, 2009, 4:07:36 PM2/27/09
to xuggle...@googlegroups.com

Art Clarke

unread,
Feb 27, 2009, 7:27:42 PM2/27/09
to xuggle...@googlegroups.com
Hi Leo,

The issue with this file is it has 5.1 audio, and our resample can only support stereo or mono.  I filed a bug for this:
http://code.google.com/p/xuggle/issues/detail?id=70

And we'll see if it's fixable.

- Art
Reply all
Reply to author
Forward
0 new messages