package org.jcodec.api.specific; import static org.jcodec.codecs.h264.H264Utils.splitMOVPacket; import org.jcodec.api.FrameGrab.MediaInfo; import org.jcodec.codecs.h264.H264Decoder; import org.jcodec.codecs.h264.H264Utils; import org.jcodec.codecs.h264.io.model.SeqParameterSet; import org.jcodec.codecs.h264.mp4.AvcCBox; import org.jcodec.common.model.ColorSpace; import org.jcodec.common.model.Packet; import org.jcodec.common.model.Picture; import org.jcodec.common.model.Size; import org.jcodec.containers.mp4.MP4Packet; import org.jcodec.containers.mp4.boxes.Box; import org.jcodec.containers.mp4.boxes.PixelAspectExt; import org.jcodec.containers.mp4.boxes.SampleEntry; import org.jcodec.containers.mp4.boxes.VideoSampleEntry; import org.jcodec.containers.mp4.demuxer.AbstractMP4DemuxerTrack; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * High level frame grabber helper. * * @author The JCodec project * */ public class AVCMP4Adaptor implements ContainerAdaptor { private H264Decoder decoder; private SampleEntry[] ses; private AvcCBox avcCBox; private int curENo; private Size size; public AVCMP4Adaptor(SampleEntry[] ses) { this.ses = ses; this.curENo = -1; calcBufferSize(); } private void calcBufferSize() { int w = Integer.MIN_VALUE, h = Integer.MIN_VALUE; for (SampleEntry se : ses) { if ("avc1".equals(se.getFourcc())) { AvcCBox avcC = H264Utils.parseAVCC((VideoSampleEntry) se); for (SeqParameterSet sps : H264Utils.readSPS(avcC.getSpsList())) { int ww = sps.pic_width_in_mbs_minus1 + 1; if (ww > w) w = ww; int hh = H264Utils.getPicHeightInMbs(sps); if (hh > h) h = hh; } } } size = new Size(w << 4, h << 4); } public AVCMP4Adaptor(AbstractMP4DemuxerTrack vt) { this(((AbstractMP4DemuxerTrack) vt).getSampleEntries()); } public Picture decodeFrame(Packet packet, int[][] data) { updateState(packet); Picture pic = ((H264Decoder) decoder).decodeFrame(H264Utils.splitMOVPacket(packet.getData(), avcCBox), data); PixelAspectExt pasp = Box.findFirst(ses[curENo], PixelAspectExt.class, "pasp"); if (pasp != null) { // TODO: transform } return pic; } private void updateState(Packet packet) { int eNo = ((MP4Packet) packet).getEntryNo(); if (eNo != curENo) { curENo = eNo; avcCBox = H264Utils.parseAVCC((VideoSampleEntry) ses[curENo]); decoder = new H264Decoder(); ((H264Decoder) decoder).addSps(avcCBox.getSpsList()); ((H264Decoder) decoder).addPps(avcCBox.getPpsList()); } } @Override public boolean canSeek(Packet pkt) { updateState(pkt); return H264Utils.idrSlice(splitMOVPacket(pkt.getData(), avcCBox)); } @Override public int[][] allocatePicture() { return Picture.create(size.getWidth(), size.getHeight(), ColorSpace.YUV444).getData(); } @Override public MediaInfo getMediaInfo() { return new MediaInfo(size); } }