Skip to content

Instantly share code, notes, and snippets.

@NeatMonster
Created April 12, 2013 20:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NeatMonster/cd3c2ec3d03ab2ce3ab3 to your computer and use it in GitHub Desktop.
Save NeatMonster/cd3c2ec3d03ab2ce3ab3 to your computer and use it in GitHub Desktop.
The subclass in charge of decoding the stream.
class DecodingThread extends Thread {
private AudioFormat audioFormat;
private IStreamCoder audioStreamCoder;
private int audioStreamIndex = -1;
private IContainer container;
private boolean exit;
private IPacket packet;
private long pausedTime;
private final RenderDisplay render;
private long startAudio;
private long startAudioTime;
private long startVideo;
private long startVideoTime;
private String url;
private IVideoResampler videoResampler;
private IStreamCoder videoStreamCoder;
private int videoStreamIndex = -1;
private DecodingThread(final RenderDisplay render) {
this.render = render;
}
//private AudioFormat getAudioFormat() {
// return audioFormat;
//}
private boolean isFuture(final IAudioSamples samples) {
return (samples.getTimeStamp() - startAudio) / 1000L > System.currentTimeMillis() - startAudioTime;
}
private boolean isFuture(final IVideoPicture picture) {
return (picture.getTimeStamp() - startVideo) / 1000L > System.currentTimeMillis() - startVideoTime;
}
@Override
public void run() {
if (!IVideoResampler.isSupported(IVideoResampler.Feature.FEATURE_COLORSPACECONVERSION))
throw new RuntimeException("Xuggler not installed!");
container = IContainer.make();
if (container.open(url, IContainer.Type.READ, null) < 0)
throw new IllegalArgumentException("invalid url!");
for (int i = 0; i < container.getNumStreams(); i++) {
final IStreamCoder coder = container.getStream(i).getStreamCoder();
if (videoStreamIndex == -1 && coder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
videoStreamIndex = i;
videoStreamCoder = coder;
} else if (audioStreamIndex == -1 && coder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO) {
audioStreamIndex = i;
audioStreamCoder = coder;
}
}
if (videoStreamIndex == -1 && audioStreamIndex == -1)
throw new RuntimeException("no video/audio stream!");
if (videoStreamCoder != null) {
if (videoStreamCoder.open() < 0)
throw new RuntimeException("no video decoder!");
if (videoStreamCoder.getPixelType() != IPixelFormat.Type.BGRA) {
videoResampler = IVideoResampler.make(videoStreamCoder.getWidth(), videoStreamCoder.getHeight(), IPixelFormat.Type.BGRA,
videoStreamCoder.getWidth(), videoStreamCoder.getHeight(), videoStreamCoder.getPixelType());
if (videoResampler == null)
throw new RuntimeException("wrong colorspace!");
} else
videoResampler = null;
}
final List<byte[]> cache = new LinkedList<byte[]>();
if (audioStreamCoder != null) {
if (audioStreamCoder.open() < 0)
throw new RuntimeException("no audio decoder!");
// TODO
audioFormat = new AudioFormat(audioStreamCoder.getSampleRate(),
16, audioStreamCoder.getChannels(), true, false);
final Entity player = Minecraft.getMinecraft().thePlayer;
SoundManager.sndSystem.rawDataStream(audioFormat, true, "youtube", (float) player.posX, (float) player.posY, (float) player.posZ, 2, 64);
}
packet = IPacket.make();
while (!exit && container.readNextPacket(packet) >= 0) {
if (packet.getStreamIndex() == videoStreamIndex) {
final IVideoPicture picture = IVideoPicture.make(videoStreamCoder.getPixelType(), videoStreamCoder.getWidth(),
videoStreamCoder.getHeight());
int offset = 0;
while (offset < packet.getSize()) {
final int bytesDecoded = videoStreamCoder.decodeVideo(picture, packet, offset);
if (bytesDecoded < 0)
throw new RuntimeException("failed decoding a packet!");
offset += bytesDecoded;
if (picture.isComplete()) {
IVideoPicture newPicture = picture;
if (videoResampler != null) {
newPicture = IVideoPicture.make(videoResampler.getOutputPixelFormat(), picture.getWidth(), picture.getHeight());
if (videoResampler.resample(newPicture, picture) < 0)
throw new RuntimeException("failed resampling!");
}
if (newPicture.getPixelType() != IPixelFormat.Type.BGRA)
throw new RuntimeException("failed converting colors!");
if (startVideoTime == 0L || startVideo == Global.NO_PTS) {
startVideoTime = System.currentTimeMillis();
startVideo = newPicture.getTimeStamp();
}
try {
render.videoQueue.put(newPicture);
} catch (final InterruptedException e) {
// e.printStackTrace();
}
}
}
} else if (packet.getStreamIndex() == audioStreamIndex) {
final IAudioSamples samples = IAudioSamples.make(1024, audioStreamCoder.getChannels());
int offset = 0;
while (offset < packet.getSize()) {
final int bytesDecoded = audioStreamCoder.decodeAudio(samples, packet, offset);
if (bytesDecoded < 0)
throw new RuntimeException("failed decoding a packet.");
offset += bytesDecoded;
if (samples.isComplete()) {
if (startAudioTime == 0L || startAudio == Global.NO_PTS) {
startAudioTime = System.currentTimeMillis();
startAudio = samples.getTimeStamp();
}
// TODO
cache.add(samples.getData().getByteArray(0, samples.getSize()));
if (cache.size() > 999) {
for (final byte[] data : cache)
SoundManager.sndSystem.feedRawAudioData("youtube", data);
cache.clear();
}
//try {
// render.audioQueue.put(samples);
//} catch (final InterruptedException e) {
// // e.printStackTrace();
//}
}
}
}
}
if (videoStreamCoder != null) {
videoStreamCoder.close();
videoStreamCoder = null;
}
if (audioStreamCoder != null) {
audioStreamCoder.close();
audioStreamCoder = null;
}
if (container != null) {
container.close();
container = null;
}
}
private void setPaused(final boolean paused) {
if (paused)
pausedTime = System.currentTimeMillis();
else {
startVideoTime += System.currentTimeMillis() - pausedTime;
startAudioTime += System.currentTimeMillis() - pausedTime;
}
}
private void setUrl(final String url) {
this.url = url;
exit = false;
pausedTime = 0L;
startVideoTime = 0L;
startAudioTime = 0L;
startVideo = Global.NO_PTS;
startAudio = Global.NO_PTS;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment