package org.jcodec.player; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.sound.sampled.AudioFormat; import org.jcodec.common.NIOUtils; import org.jcodec.common.model.AudioFrame; import org.jcodec.common.model.Frame; import org.jcodec.common.model.Picture; import org.jcodec.common.model.RationalLarge; import org.jcodec.common.model.Size; import org.jcodec.player.Player.Listener; import org.jcodec.player.filters.AudioOut; import org.jcodec.player.filters.MediaInfo.AudioInfo; import org.jcodec.player.filters.MediaInfo.VideoInfo; import org.jcodec.player.filters.VideoOutput; import org.jcodec.player.filters.VideoSource; import org.jcodec.player.filters.audio.AudioSource; import org.jcodec.scale.ColorUtil; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * Media step engine * * @author The JCodec project * */ public class Stepper { private VideoSource videoSource; private AudioSource audioSource; private VideoOutput vo; private AudioOut ao; private VideoInfo mi; private AudioInfo ai; private Picture dst; private RationalLarge pts; // private int frameNo; private int[][] target; private List<Player.Listener> listeners = new ArrayList<Player.Listener>(); private Timer timer = new Timer(); private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private AudioFormat af; public Stepper(VideoSource videoSource, AudioSource audioSource, VideoOutput vo, AudioOut ao) throws IOException { this.videoSource = videoSource; this.vo = vo; this.ao = ao; this.audioSource = audioSource; this.mi = videoSource.getMediaInfo(); ai = this.audioSource.getAudioInfo(); af = ai.getFormat(); ao.open(af, af.getFrameSize() * (int) af.getFrameRate()); } private int[][] createTarget() { Size dim = mi.getDim(); int sz = 2 * dim.getWidth() * dim.getHeight(); return new int[][] { new int[sz], new int[sz], new int[sz] }; } private void nextInt() throws IOException { // frameNo++; // curInt(); // } // private void curInt() throws IOException { if (target == null) target = createTarget(); // System.out.println(frameNo); // videoSource.gotoFrame(frameNo); Frame decode = videoSource.decode(target); RationalLarge pts = decode.getPts(); if (audioSource.drySeek(pts)) { audioSource.seek(pts); playSound(150); } show(decode); } // private void prevInt() throws IOException { // frameNo--; // curInt(); // } private void show(Frame frame) { pts = frame.getPts(); fireTimeEvent(frame); Picture src = frame.getPic(); if (src.getColor() != vo.getColorSpace()) { if (dst == null || dst.getWidth() != src.getWidth() || dst.getHeight() != src.getHeight()) dst = Picture.create(src.getWidth(), src.getHeight(), vo.getColorSpace()); ColorUtil.getTransform(src.getColor(), vo.getColorSpace()).transform(src, dst); vo.show(dst, frame.getPixelAspect()); } else { vo.show(src, frame.getPixelAspect()); } } private void fireTimeEvent(final Frame frame) { timer.schedule(new TimerTask() { public void run() { for (final Listener listener : listeners) { try { listener.timeChanged(frame.getPts(), frame.getFrameNo(), frame.getTapeTimecode()); } catch (Throwable t) { t.printStackTrace(); } } } }, 0); } private void playSound(int ms) throws IOException { ByteBuffer sound = ByteBuffer.allocate(((int) (ms * af.getFrameRate() / 1000) * af.getFrameSize())); ByteBuffer buf = ByteBuffer.allocate(48000 * af.getFrameSize()); while (sound.remaining() > 0) { AudioFrame frame = audioSource.getFrame(buf); if (frame == null) break; ByteBuffer data = frame.getData(); NIOUtils.write(sound, NIOUtils.read(data, Math.min(data.remaining(), sound.remaining()))); } sound.flip(); ao.flush(); ao.resume(); ao.write(sound); ao.drain(); ao.pause(); } public void next() { executor.submit(new Runnable() { public void run() { try { nextInt(); } catch (Throwable t) { t.printStackTrace(); } } }); } public void prev() { // executor.submit(new Runnable() { // public void run() { // try { // prevInt(); // } catch (Throwable t) { // t.printStackTrace(); // } // } // }); } public RationalLarge getPos() { return pts; } public void gotoFrame(int frame) { videoSource.gotoFrame(frame); // frameNo = frame; } public void setListeners(List<Player.Listener> listeners) { this.listeners = listeners; } }