byte[] GetDataBytes(TPendingSample Sample) { var MdatIndex = Sample.MdatIndex; //Sample.DataPosition, Sample.DataSizeuint MdatIndex, long Position, long Size) if (!HasMdat(MdatIndex)) { throw new System.Exception("Not yet recieved mdat #" + MdatIndex); } var Meta = Mdats[MdatIndex]; long Position; if (Sample.DataFilePosition.HasValue) { Position = Sample.DataFilePosition.Value - Meta.Atom.AtomDataFilePosition; } else { Position = Sample.DataPosition.Value; } return(Meta.Bytes.SubArray(Position, Sample.DataSize)); }
void ParseNextMp4Header() { // check if there's more data to be read var KnownFileSize = GetKnownFileSize(); if (Mp4BytesRead >= KnownFileSize) { return; } int TimeOffset = 0; System.Action <PopX.Mpeg4.TTrack> EnumTrack = (Track) => { System.Action <byte[], int> PushPacket; byte[] Sps_AnnexB; byte[] Pps_AnnexB; // track has header (so if we have moov and moof's, this only comes up once, and that's when we need to decode SPS/PPS if (Track.SampleDescriptions != null) { if (Track.SampleDescriptions[0].Fourcc != "avc1") { throw new System.Exception("Expecting fourcc avc1, got " + Track.SampleDescriptions[0].Fourcc); } H264.AvccHeader Header; Header = PopX.H264.ParseAvccHeader(Track.SampleDescriptions[0].AvccAtomData); // wrong place to assign this! should be when we assign the track index H264TrackHeader = Header; var Pps = new List <byte>(new byte[] { 0, 0, 0, 1 }); Pps.AddRange(Header.PPSs[0]); Pps_AnnexB = Pps.ToArray(); var Sps = new List <byte>(new byte[] { 0, 0, 0, 1 }); Sps.AddRange(Header.SPSs[0]); Sps_AnnexB = Sps.ToArray(); PushPacket = (Packet, FrameNumber) => { H264.AvccToAnnexb4(Header, Packet, (Bytes) => { PushFrame_AnnexB(Bytes, FrameNumber); }); }; } else if (Preconfigured_SPS_Bytes != null && Preconfigured_SPS_Bytes.Length > 0) { throw new System.Exception("Need to refactor to process preconfigured SPS before handling tracks"); // split this header Sps_AnnexB = Preconfigured_SPS_Bytes; Pps_AnnexB = null; // gr: turns out these are AVCC, not annexb // gr: should be able to auto detect without header //PushPacket = PushAnnexB; H264.AvccHeader Header = new H264.AvccHeader(); Header.NaluLength = 2; PushPacket = (Packet, FrameNumber) => { H264.AvccToAnnexb4(Header, Packet, (Bytes) => { PushFrame_AnnexB(Bytes, FrameNumber); }); }; } else { // track without header, assume it's already come via moov and this is a moof Sps_AnnexB = null; Pps_AnnexB = null; PushPacket = null; } // load h264 header if (Sps_AnnexB != null) { H264.Profile Profile; float Level; PopX.H264.GetProfileLevel(Sps_AnnexB, out Profile, out Level); if (Profile != H264.Profile.Baseline) { Debug.LogWarning("PopH264 currently only supports baseline profile. This is " + Profile + " level=" + Level); } PushFrame_AnnexB(Sps_AnnexB, 0); PushFrame_AnnexB(Pps_AnnexB, 0); } // load samples if (Track.Samples == null) { if (VerboseDebug) { Debug.Log("Mp4 Track with null samples (next mdat=" + NextMdat + ")"); } } else { // gr: is there a better mdat ident? don't think so, it's just the upcoming one in mpeg sequence // gr: sometimes, the mdat is before the tracks... // gr: the sample offset also needs correcting var MdatIndex = MDatBeforeTrack ? NextMdat - 1 : 0; if (VerboseDebug) { Debug.Log("Found mp4 track " + Track.Samples.Count + " for next mdat: " + MdatIndex); } if (Track.Samples.Count > 0) { var LastSampleTime = Track.Samples[Track.Samples.Count - 1].PresentationTimeMs; if (!LastFrameTime.HasValue) { LastFrameTime = LastSampleTime; } LastFrameTime = Mathf.Max(LastFrameTime.Value, LastSampleTime); } foreach (var Sample in Track.Samples) { try { var NewSample = new TPendingSample(); if (PendingInputSamples == null) { PendingInputSamples = new List <TPendingSample>(); } NewSample.Sample = Sample; NewSample.MdatIndex = MdatIndex; //NewSample.DataPosition = Sample.DataPosition; //NewSample.DataFilePosition = Sample.DataFilePosition; //NewSample.DataSize = Sample.DataSize; var TimeOffsetMs = (int)(TimeOffset / 10000.0f); //NewSample.PresentationTime = Sample.PresentationTimeMs + TimeOffsetMs; NewSample.Format = PacketFormat.Avcc; PendingInputSamples.Add(NewSample); } catch (System.Exception e) { Debug.LogException(e); break; } } } }; System.Action <List <PopX.Mpeg4.TTrack> > EnumTracks = (Tracks) => { // moof headers don't have track headers, so we should have our track by now // this track may be the original though, so hunt down the h264 one if (!H264TrackIndex.HasValue) { for (int t = 0; t < Tracks.Count; t++) { var Track = Tracks[t]; if (Track.SampleDescriptions == null) { continue; } if (Track.SampleDescriptions[0].Fourcc != "avc1") { Debug.Log("Skipping track codec: " + Track.SampleDescriptions[0].Fourcc); continue; } H264TrackIndex = t; } } if (!H264TrackIndex.HasValue) { throw new System.Exception("Couldn't find avc1 track"); } EnumTrack(Tracks[H264TrackIndex.Value]); }; System.Action <PopX.TAtom> EnumMdat = (MdatAtom) => { if (VerboseDebug) { Debug.Log("EnumMdat( NextMdat=" + NextMdat); } if (!H264TrackIndex.HasValue) { MDatBeforeTrack = true; } // this is the meta for the pending mdat var MdatIndex = NextMdat; if (Mdats == null) { Mdats = new Dictionary <uint, MdatBlock>(); } var Mdat = new MdatBlock(); Mdat.Atom = MdatAtom; Mdat.FileOffset = Mdat.Atom.AtomDataFilePosition; // need a better way to do this if (VerboseDebug) { Debug.Log("Got MDat " + MdatIndex + " x" + Mdat.Bytes.Length + " bytes"); } Mdats.Add(MdatIndex, Mdat); // increment once everything succeeds NextMdat++; }; // ideally only once we've verified we have an mp4, but before moov. Maybe just if an ftyp is found if (Decoder == null) { Decoder = new PopH264.Decoder(DecoderParams, DecodeOnSeperateThread); } System.Func <long, byte[]> PopData = (long DataSize) => { var Data = ReadFileFunction(Mp4BytesRead, DataSize); Mp4BytesRead += DataSize; return(Data); }; try { PopX.Mpeg4.ParseNextAtom(PopData, Mp4BytesRead, EnumTracks, EnumMdat); } catch (System.Exception e) { Debug.LogException(e); } }