package com.googlecode.mp4parser.authoring.tracks;
import com.coremedia.iso.IsoBufferWrapper;
import com.coremedia.iso.IsoBufferWrapperImpl;
import com.coremedia.iso.IsoFile;
import com.coremedia.iso.IsoOutputStream;
import com.coremedia.iso.MultiplexIsoBufferWrapperImpl;
import com.coremedia.iso.boxes.CompositionTimeToSample;
import com.coremedia.iso.boxes.SampleDependencyTypeBox;
import com.coremedia.iso.boxes.SampleDescriptionBox;
import com.coremedia.iso.boxes.TimeToSampleBox;
import com.googlecode.mp4parser.authoring.AbstractTrack;
import com.googlecode.mp4parser.authoring.TrackMetaData;
import com.googlecode.mp4parser.h264.*;
import com.googlecode.mp4parser.h264.model.*;
import com.googlecode.mp4parser.h264.read.CAVLCReader;
import com.googlecode.mp4parser.h264.read.SliceHeaderReader;
import com.googlecode.mp4parser.util.Path;
//import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
*
*/
public class RawH264Track extends AbstractTrack implements StreamParams {
private static int a;
List<IsoBufferWrapper> samples = new ArrayList<IsoBufferWrapper>();
StreamParams streamParams;
double fps = 30;
public static void main(String[] args) throws IOException {
IsoFile isoFile = new IsoFile(new IsoBufferWrapperImpl(new File("/home/sannies/vw.mp4")));
Path p = new Path(isoFile);
isoFile.parse();
CompositionTimeToSample ctts = (CompositionTimeToSample) p.getPath("/moov/trak[1]/mdia/minf/stbl/ctts");
int[] cts = CompositionTimeToSample.blowupCompositionTimes(ctts.getEntries());
RawH264Track track = new RawH264Track(new IsoBufferWrapperImpl(new File("/home/sannies/vw_track2.h264")));
//RawH264Track track = new RawH264Track(new IsoBufferWrapperImpl(new File("/home/sannies/suckerpunch-samurai_h640w_track1.h264")));
}
public RawH264Track(IsoBufferWrapperImpl rawH264) throws IOException {
InnerAccessUnit current = new InnerAccessUnit();
InnerAccessUnit previous = new InnerAccessUnit();
NALUnitReader nalUnitReader = new AnnexBNALUnitReader(rawH264);
AccessUnitSourceImpl accessUnitSource = new AccessUnitSourceImpl(nalUnitReader);
streamParams = accessUnitSource;
SliceHeaderReader sliceHeaderReader = new SliceHeaderReader(accessUnitSource);
AccessUnit au;
long frameNumInGop = 0;
while ((au = accessUnitSource.nextAccessUnit()) != null) {
//System.err.println("Start AU");
List<IsoBufferWrapper> nals = new LinkedList<IsoBufferWrapper>();
IsoBufferWrapper nalUnitBuffer;
while ((nalUnitBuffer = au.nextNALUnit()) != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new IsoOutputStream(baos).writeUInt32(nalUnitBuffer.size());
nals.add(new IsoBufferWrapperImpl(baos.toByteArray()));
nals.add(nalUnitBuffer);
NALUnit nalUnit = NALUnit.read(nalUnitBuffer);
if (nalUnit.type == NALUnitType.IDR_SLICE ||
nalUnit.type == NALUnitType.NON_IDR_SLICE ||
nalUnit.type == NALUnitType.AUX_SLICE ||
nalUnit.type == NALUnitType.SLICE_PART_A ||
nalUnit.type == NALUnitType.SLICE_PART_B ||
nalUnit.type == NALUnitType.SLICE_PART_C) {
current.sliceHeaders.add(sliceHeaderReader.read(nalUnit, new CAVLCReader(nalUnitBuffer)));
}
if (++a % 1000 == 0) {
System.err.println(a);
}
}
samples.add(new MultiplexIsoBufferWrapperImpl(nals));
decodedPoc(current, previous);
if (current.poc == 0) {
frameNumInGop = 0;
}
System.err.println("cts: " + ( current.poc - frameNumInGop * 2 ));
previous = current;
frameNumInGop++;
current = new InnerAccessUnit();
}
}
public List<IsoBufferWrapper> getSamples() {
return samples;
}
public SampleDescriptionBox getSampleDescriptionBox() {
SampleDescriptionBox sampleDescriptionBox = new SampleDescriptionBox();
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
/*
MP4AV_calculate_dts_from_pts in mp4creator's mpeg4.cpp
*/
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public long[] getSyncSamples() {
return new long[0]; //To change body of implemented methods use File | Settings | File Templates.
}
public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public TrackMetaData getTrackMetaData() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public Type getType() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public SeqParameterSet getSPS(int id) {
return streamParams.getSPS(id);
}
public PictureParameterSet getPPS(int id) {
return streamParams.getPPS(id);
}
static class InnerAccessUnit {
int decodingTime;
int picOrderCntMsb;
int poc;
SEI sei;
List<SliceHeader> sliceHeaders = new LinkedList<SliceHeader>();
}
static InnerAccessUnit decodePocType0(InnerAccessUnit current, InnerAccessUnit prev) {
int TopFieldOrderCnt = Integer.MAX_VALUE;
int BottomFieldOrderCnt = Integer.MAX_VALUE;
int prevPicOrderCntMsb;
int prevPicOrderCntLsb;
if (current.sliceHeaders.get(0).slice_type == SliceType.I || current.sliceHeaders.get(0).slice_type == SliceType.SI) {
prevPicOrderCntMsb = 0;
prevPicOrderCntLsb = 0;
} else {
// if memory management dunno
if (current.sei != null) {
for (SEI.SEIMessage message : current.sei.messages) {
if (message.payloadType == SEI.PIC_TIMING) {
throw new RuntimeException("That needs to be implemented. 7687526897568234.");
}
}
}
prevPicOrderCntMsb = prev.picOrderCntMsb;
prevPicOrderCntLsb = prev.sliceHeaders.get(0).pic_order_cnt_lsb;
}
int MaxPicOrderCntLsb = 1 << (current.sliceHeaders.get(0).sps.log2_max_pic_order_cnt_lsb_minus4 + 4);
int picOrderCntMsb;
if ((current.sliceHeaders.get(0).pic_order_cnt_lsb < prevPicOrderCntLsb) &&
((prevPicOrderCntLsb - current.sliceHeaders.get(0).pic_order_cnt_lsb) >= (MaxPicOrderCntLsb / 2))) {
picOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb;
} else if ((current.sliceHeaders.get(0).pic_order_cnt_lsb > prevPicOrderCntLsb) &&
((current.sliceHeaders.get(0).pic_order_cnt_lsb - prevPicOrderCntLsb) > (MaxPicOrderCntLsb / 2))) {
picOrderCntMsb = prevPicOrderCntMsb - MaxPicOrderCntLsb;
} else {
picOrderCntMsb = prevPicOrderCntMsb;
}
if (!current.sliceHeaders.get(0).bottom_field_flag) {
TopFieldOrderCnt = picOrderCntMsb + current.sliceHeaders.get(0).pic_order_cnt_lsb;
}
if (current.sliceHeaders.get(0).bottom_field_flag) {
if (!current.sliceHeaders.get(0).field_pic_flag) {
BottomFieldOrderCnt = TopFieldOrderCnt + current.sliceHeaders.get(0).delta_pic_order_cnt_bottom;
} else {
BottomFieldOrderCnt = picOrderCntMsb + current.sliceHeaders.get(0).pic_order_cnt_lsb;
}
}
if (current.sliceHeaders.get(0).sps.frame_mbs_only_flag || !current.sliceHeaders.get(0).field_pic_flag)
current.poc = Math.min(TopFieldOrderCnt, BottomFieldOrderCnt);
else if (current.sliceHeaders.get(0).bottom_field_flag)
current.poc = BottomFieldOrderCnt;
else
current.poc = TopFieldOrderCnt;
// System.err.println("poc : " + current.poc);
// System.err.println("poc-diff: " + (current.poc - prev.poc));
return current;
}
static InnerAccessUnit decodePocType1(InnerAccessUnit current, InnerAccessUnit prev) {
System.err.println("decodePocType1");
throw new UnsupportedOperationException("Please implement decodePocType1");
}
static InnerAccessUnit decodePocType2(InnerAccessUnit current, InnerAccessUnit prev) {
System.err.println("decodePocType2");
throw new UnsupportedOperationException("Please implement decodePocType2");
}
static InnerAccessUnit insertDecodingTime(InnerAccessUnit current, InnerAccessUnit previous) {
int DeltaTfiDivisorIdx;
if (!current.sliceHeaders.get(0).sps.vuiParams.pic_struct_present_flag) {
DeltaTfiDivisorIdx = 1 + (1 - (!current.sliceHeaders.get(0).field_pic_flag ? 0 : 1));
} else {
throw new UnsupportedOperationException("Hmm I cannot deal with picTimingSei");
// get details here
// D.1.2 Picture timing SEI message syntax
/*SEI.SEIMessage picTimingSei = null;
for (SEI.SEIMessage message : current.sei.messages) {
if (message.payloadType == 1) {
picTimingSei = message;
}
}
if (picTimingSei != null) {
if (!avc.sei.pic_timing.pic_struct)
DeltaTfiDivisorIdx = 2;
else if (avc.sei.pic_timing.pic_struct == 8)
DeltaTfiDivisorIdx = 6;
else
DeltaTfiDivisorIdx = (avc.sei.pic_timing.pic_struct + 1) / 2;
} */
}
current.decodingTime = previous.decodingTime + 2 * current.sliceHeaders.get(0).sps.vuiParams.num_units_in_tick * DeltaTfiDivisorIdx;
//System.err.print("FPS: " + 2 * current.sliceHeaders.get(0).sps.vuiParams.time_scale);
return current;
}
static InnerAccessUnit decodedPoc(InnerAccessUnit current, InnerAccessUnit previous) {
insertDecodingTime(current, previous);
switch (current.sliceHeaders.get(0).sps.pic_order_cnt_type) {
case 0:
return decodePocType0(current, previous);
case 1:
return decodePocType1(current, previous);
case 2:
return decodePocType2(current, previous);
default:
return null;
}
}
public void setFps(double fps) {
this.fps = fps;
}
}