gps/GPSResources/tcpmp 0.73/splitter/mov.c

1601 lines
40 KiB
C
Executable File

/*****************************************************************************
*
* This program is free software ; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: mov.c 607 2006-01-22 20:58:29Z picard $
*
* The Core Pocket Media Player
* Copyright (c) 2004-2005 Gabor Kovacs
*
****************************************************************************/
#include "../common/common.h"
#include "../common/zlib/zlib.h"
#include "mov.h"
#include "movpal.h"
#define MP4_INVALID_AUDIO_TYPE 0x00
#define MP4_MPEG1_AUDIO_TYPE 0x6B
#define MP4_MPEG2_AUDIO_TYPE 0x69
#define MP4_MPEG2_AAC_MAIN_AUDIO_TYPE 0x66
#define MP4_MPEG2_AAC_LC_AUDIO_TYPE 0x67
#define MP4_MPEG2_AAC_SSR_AUDIO_TYPE 0x68
#define MP4_MPEG4_AUDIO_TYPE 0x40
#define MP4_PRIVATE_AUDIO_TYPE 0xE0
#define MP4_INVALID_VIDEO_TYPE 0x00
#define MP4_MPEG1_VIDEO_TYPE 0x6A
#define MP4_MPEG2_SIMPLE_VIDEO_TYPE 0x60
#define MP4_MPEG2_MAIN_VIDEO_TYPE 0x61
#define MP4_MPEG2_SNR_VIDEO_TYPE 0x62
#define MP4_MPEG2_SPATIAL_VIDEO_TYPE 0x63
#define MP4_MPEG2_HIGH_VIDEO_TYPE 0x64
#define MP4_MPEG2_442_VIDEO_TYPE 0x65
#define MP4_MPEG4_VIDEO_TYPE 0x20
#define MP4_JPEG_VIDEO_TYPE 0x6C
typedef struct mp4stsc
{
int FirstChunk;
int Samples;
} mp4stsc;
typedef struct mp4stts
{
int Count;
int Delta;
} mp4stts;
typedef struct mp4streampos
{
int CurrentTime;
int SampleNo;
int SamplesLeft;
int STSSDelta;
int STSSValue;
int STSZDelta;
int STSZValue;
int STTSLeft;
const uint8_t* STSSPos;
const uint8_t* STSSEnd;
const mp4stts* STTSPos;
const mp4stsc* STSCPos;
const uint8_t* STSZPos;
const uint8_t* STSZEnd;
const int* STCOPos;
} mp4streampos;
typedef struct mp4stream
{
format_stream Stream;
tick_t Duration;
int TimeScale;
int SamplesPerPacket;
int BytesPerPacket;
bool_t AllKey;
filepos_t OffsetMin;
filepos_t OffsetMax;
int StartTime;
mp4streampos Pos;
int STSZDef; //default sample size
array STSS; //key samples (compressed)
array STSZ; //sample size (compressed)
array STTS; //sample duration (mp4stts) + delimiter
array STSC; //sample per chunk (mp4stsc) + delimiter
array STCO; //chunk offset (int) + delimiter
} mp4stream;
typedef struct mp4
{
format_base Format;
uint32_t Type;
uint32_t CompressType;
boolmem_t ErrorShowed;
boolmem_t MOOVFound;
boolmem_t RMRAFound;
int TimeScale;
mp4stream* Track;
tchar_t Meta[32];
} mp4;
typedef void (*mp4atomfunc)(mp4* p, filepos_t EndOfAtom);
typedef struct mp4atomtable
{
uint32_t Atom;
mp4atomfunc Func;
} mp4atomtable;
typedef struct mp4atom
{
uint32_t Atom;
filepos_t EndOfAtom;
} mp4atom;
static int ReadVersion(format_reader* Reader)
{
int Ver = Reader->Read8(Reader);
Reader->Skip(Reader,3); //flags;
return Ver;
}
static bool_t ReadAtom(format_reader* Reader, mp4atom* Atom)
{
filepos_t Size;
Atom->Atom = 0;
Atom->EndOfAtom = Reader->FilePos;
Size = Reader->ReadBE32(Reader);
Reader->Read(Reader,&Atom->Atom,sizeof(Atom->Atom));
if (Size == 1)
Size = (filepos_t)Reader->ReadBE64(Reader);
Atom->EndOfAtom += Size;
return Size >= 8;
}
static void ReadSubAtoms(mp4* p,filepos_t EndOfAtom);
static INLINE format_reader* Reader(mp4* p) { return p->Format.Reader; }
static void ReadFTYP(mp4* p, filepos_t EndOfAtom)
{
p->Type = 0;
Reader(p)->Read(Reader(p),&p->Type,sizeof(p->Type));
}
static void ReadRMRA(mp4* p, filepos_t EndOfAtom)
{
p->RMRAFound = 1;
ReadSubAtoms(p,EndOfAtom);
p->RMRAFound = 0;
}
static void ReadMOOV(mp4* p, filepos_t EndOfAtom)
{
p->MOOVFound = 1;
ReadSubAtoms(p,EndOfAtom);
}
static INLINE const uint8_t* ReadValue(const uint8_t* p,const uint8_t* pe,int* Delta,int* Value,bool_t Alternate)
{
if (p>=pe)
*Value = 0;
else
{
int i = *(int8_t*)(p++);
while (!(i & 1))
i = (i<<7) | *(p++);
i >>= 1;
if (Alternate) *Delta = -*Delta;
*Delta += i;
*Value += *Delta;
}
return p;
}
static void WriteValue(array* Array,int* Delta,int* Value,int i,bool_t Alternate)
{
uint8_t Data[8];
int n,Flag = 1;
i -= *Value;
*Value += i;
if (Alternate) *Delta = -*Delta;
i -= *Delta;
*Delta += i;
for (n=7;i>=64||i<-64;i>>=7,--n)
{
Data[n] = (uint8_t)(((i & 127) << 1)|Flag);
Flag = 0;
}
Data[n] = (uint8_t)((i << 1)|Flag);
ArrayAppend(Array,Data+n,8-n,1024);
}
static NOINLINE void NextKey(mp4streampos* Pos)
{
if (!Pos->STSSPos)
++Pos->STSSValue;
else
Pos->STSSPos = ReadValue(Pos->STSSPos,Pos->STSSEnd,&Pos->STSSDelta,&Pos->STSSValue,0);
}
static int NextSample(mp4stream* Stream,mp4streampos* Pos)
{
if (Stream->Stream.Fragmented || Stream->Stream.ForceMerge)
{
int n,i;
for (n=Pos->SamplesLeft;n>0;n-=i)
{
if (!Pos->STTSLeft)
Pos->STTSLeft = (++Pos->STTSPos)->Count;
i = min(Pos->STTSLeft,n);
Pos->STTSLeft -= i;
Pos->CurrentTime += i*Pos->STTSPos->Delta;
}
n = Pos->SamplesLeft;
Pos->SampleNo += n;
Pos->SamplesLeft = 0;
if (Stream->STSZDef==1 && Stream->SamplesPerPacket && Stream->BytesPerPacket)
return Scale(n,Stream->BytesPerPacket,Stream->SamplesPerPacket);
return Stream->STSZDef*n;
}
else
{
if (!Pos->STTSLeft)
Pos->STTSLeft = (++Pos->STTSPos)->Count;
--Pos->STTSLeft;
++Pos->SampleNo;
--Pos->SamplesLeft;
Pos->CurrentTime += Pos->STTSPos->Delta;
if (!Stream->STSZDef)
Pos->STSZPos = ReadValue(Pos->STSZPos,Pos->STSZEnd,&Pos->STSZDelta,&Pos->STSZValue,1);
return Pos->STSZValue;
}
}
static void NextChunk(mp4stream* Stream,mp4streampos* Pos)
{
// return sample count in chunk and step to next chunk
do
{
int Chunk = Pos->STCOPos - ARRAYBEGIN(Stream->STCO,int);
while (Pos->STSCPos[1].FirstChunk <= Chunk)
++Pos->STSCPos;
++Pos->STCOPos;
Pos->SamplesLeft = Pos->STSCPos->Samples;
} while (Pos->SamplesLeft <= 0);
}
static void Head(mp4stream* Stream,mp4streampos* Pos)
{
Pos->SampleNo = 0;
Pos->CurrentTime = Stream->StartTime;
Pos->SamplesLeft = 0;
Pos->STTSLeft = 0;
Pos->STSSDelta = 0;
Pos->STSSValue = 0;
Pos->STSZDelta = 0;
Pos->STSZValue = 0;
Pos->STSSPos = ARRAYBEGIN(Stream->STSS,uint8_t);
Pos->STSSEnd = ARRAYEND(Stream->STSS,uint8_t);
Pos->STTSPos = ARRAYBEGIN(Stream->STTS,mp4stts);
Pos->STSCPos = ARRAYBEGIN(Stream->STSC,mp4stsc);
Pos->STSZPos = ARRAYBEGIN(Stream->STSZ,uint8_t);
Pos->STSZEnd = ARRAYEND(Stream->STSZ,uint8_t);
Pos->STCOPos = ARRAYBEGIN(Stream->STCO,int);
if (Pos->STTSPos)
Pos->STTSLeft = Pos->STTSPos->Count;
if (Stream->STSZDef)
Pos->STSZValue = Stream->STSZDef;
if (Pos->STSSPos)
NextKey(Pos);
else
if (Stream->AllKey)
Pos->STSSValue = 0;
else
Pos->STSSValue = -1;
}
static int GetFrames(mp4stream* Stream)
{
const mp4stsc* i;
int Frames = 0;
int Chunks = ARRAYCOUNT(Stream->STCO,int)-1; //minus delimier
for (i=ARRAYBEGIN(Stream->STSC,mp4stsc);i!=ARRAYEND(Stream->STSC,mp4stsc)-1;++i)
{
int Next = min(Chunks,i[1].FirstChunk);
if (Next > i->FirstChunk)
Frames += i->Samples * (Next - i->FirstChunk);
}
return Frames;
}
static void ReadTRAK(mp4* p,filepos_t EndOfAtom)
{
mp4stream* Stream;
p->Track = (mp4stream*) Format_AddStream(&p->Format,sizeof(mp4stream));
p->Track->OffsetMin = MAX_INT;
p->Track->OffsetMax = 0;
ReadSubAtoms(p,EndOfAtom);
Stream = p->Track;
if (Stream)
{
if (ARRAYEMPTY(Stream->STCO) || ARRAYEMPTY(Stream->STTS) || ARRAYEMPTY(Stream->STSC))
Format_RemoveStream(&p->Format);
else
{
if (Stream->Stream.Format.Type == PACKET_VIDEO && Stream->Duration>0)
{
// calc FPS
Stream->Stream.Format.PacketRate.Num = Scale(GetFrames(Stream),TICKSPERSEC*1000,Stream->Duration);
Stream->Stream.Format.PacketRate.Den = 1000;
if (Stream->Stream.Format.PacketRate.Num < 1000) // slideshow or podcast?
Stream->Stream.Format.Format.Video.Pixel.Flags |= PF_NOPREROTATE;
}
//todo: calc ByteRate
Head(Stream,&Stream->Pos);
Format_PrepairStream(&p->Format,&Stream->Stream);
}
p->Track = NULL;
}
}
static void ReadMETA(mp4* p,filepos_t EndOfAtom)
{
ReadVersion(Reader(p));
ReadSubAtoms(p,EndOfAtom);
}
static void ReadSTSS(mp4* p,filepos_t EndOfAtom)
{
mp4stream* Stream = p->Track;
int Count;
int Delta,Value;
if (!Stream || Stream->Stream.Fragmented) return;
ReadVersion(Reader(p));
Count = Reader(p)->ReadBE32(Reader(p));
if (Count <= 1 && Stream->Stream.Format.Type == PACKET_VIDEO &&
Stream->Stream.Format.Format.Video.Pixel.FourCC == FOURCC('S','2','6','3'))
{
// assume all frames as keyframes
Stream->AllKey = 1;
return;
}
ArrayClear(&Stream->STSS);
ArrayAlloc(&Stream->STSS,Count+128,1024);
Delta = Value = 0;
while (Count-->0)
WriteValue(&Stream->STSS,&Delta,&Value,Reader(p)->ReadBE32(Reader(p))-1,0);
ArrayLock(&Stream->STSS);
}
static void ReadSTSC(mp4* p,filepos_t EndOfAtom)
{
mp4stream* Stream = p->Track;
int Count;
if (!Stream) return;
ReadVersion(Reader(p));
Count = Reader(p)->ReadBE32(Reader(p));
ArrayClear(&Stream->STSC);
if (ArrayAppend(&Stream->STSC,NULL,sizeof(mp4stsc)*(Count+1),1))
{
mp4stsc* se = ARRAYEND(Stream->STSC,mp4stsc)-1;
mp4stsc* s;
for (s=ARRAYBEGIN(Stream->STSC,mp4stsc);s!=se;++s)
{
s->FirstChunk = Reader(p)->ReadBE32(Reader(p))-1;
s->Samples = Reader(p)->ReadBE32(Reader(p));
Reader(p)->ReadBE32(Reader(p)); // desc
}
s->FirstChunk = MAX_INT; // delimiter
s->Samples = 0;
ArrayLock(&Stream->STSC);
}
}
static void ReadSTCO(mp4* p,filepos_t EndOfAtom)
{
mp4stream* Stream = p->Track;
int Count;
if (!Stream) return;
ReadVersion(Reader(p));
Count = Reader(p)->ReadBE32(Reader(p));
ArrayClear(&Stream->STCO);
if (ArrayAppend(&Stream->STCO,NULL,sizeof(int)*(Count+1),1))
{
int* ce = ARRAYEND(Stream->STCO,int)-1;
int* c;
for (c=ARRAYBEGIN(Stream->STCO,int);c!=ce;++c)
{
*c = Reader(p)->ReadBE32(Reader(p));
// skip first and last chunks (some crappy encoders make bad interleaving, but leaving the last chunk at file end)
if (Count<3 || (c!=ARRAYBEGIN(Stream->STCO,int) && c!=ce-1))
{
if (Stream->OffsetMin > *c)
Stream->OffsetMin = *c;
if (Stream->OffsetMax < *c)
Stream->OffsetMax = *c;
}
}
*c = MAX_INT;
ArrayLock(&Stream->STCO);
}
}
static void ReadCTTS(mp4* p,filepos_t EndOfAtom)
{
// presentation time offset (TCPMP only handles decoding time at the moment)
mp4stream* Stream = p->Track;
int Count;
if (!Stream) return;
ReadVersion(Reader(p));
Count = Reader(p)->ReadBE32(Reader(p));
if (Count>0)
{
int Offset;
int Min = MAX_INT;
while (Count-->0)
{
Reader(p)->ReadBE32(Reader(p)); // samples
Offset = Reader(p)->ReadBE32(Reader(p));
if (Min > Offset)
Min = Offset;
}
if (Min>0 && Min<Stream->TimeScale)
Stream->StartTime = Min;
}
}
static void ReadSTSZ(mp4* p,filepos_t EndOfAtom)
{
mp4stream* Stream = p->Track;
int Count;
if (!Stream) return;
ReadVersion(Reader(p));
Stream->STSZDef = Reader(p)->ReadBE32(Reader(p));
Count = Reader(p)->ReadBE32(Reader(p));
if (!Stream->STSZDef)
{
int Delta,Value;
Stream->Stream.Fragmented = 0;
ArrayClear(&Stream->STSZ);
ArrayAlloc(&Stream->STSZ,(Stream->Stream.Format.Type==PACKET_VIDEO?2*Count:Count)+1024,1024);
Delta = Value = 0;
while (Count-->0)
WriteValue(&Stream->STSZ,&Delta,&Value,Reader(p)->ReadBE32(Reader(p)),1);
ArrayLock(&Stream->STSZ);
}
else
if (Stream->STSZDef==1)
Stream->Stream.Fragmented = 1;
}
static void ReadSTTS(mp4* p,filepos_t EndOfAtom)
{
mp4stream* Stream = p->Track;
int Count;
if (!Stream) return;
ReadVersion(Reader(p));
Count = Reader(p)->ReadBE32(Reader(p));
ArrayClear(&Stream->STTS);
if (ArrayAppend(&Stream->STTS,NULL,sizeof(mp4stts)*(Count+1),1))
{
mp4stts* se = ARRAYEND(Stream->STTS,mp4stts)-1;
mp4stts* s;
for (s=ARRAYBEGIN(Stream->STTS,mp4stts);s!=se;++s)
{
s->Count = Reader(p)->ReadBE32(Reader(p));
s->Delta = Reader(p)->ReadBE32(Reader(p));
}
s->Count = MAX_INT;
s->Delta = 0;
ArrayLock(&Stream->STTS);
}
}
static void ReadExtra(mp4* p,filepos_t EndOfAtom)
{
filepos_t Len = EndOfAtom - Reader(p)->FilePos;
packetformat* Format;
if (!p->Track || Len<=0) return;
Format = &p->Track->Stream.Format;
if (PacketFormatExtra(Format,Len))
Reader(p)->Read(Reader(p),Format->Extra,Format->ExtraLength);
}
static int DescLen(mp4* p)
{
int Len = 0;
int i;
for (i=0;i<4;++i)
{
int v = Reader(p)->Read8(Reader(p));
Len = (Len << 7) | (v & 0x7F);
if (!(v & 0x80))
break;
}
return Len;
}
static void ReadESDS(mp4* p,filepos_t EndOfAtom)
{
int Tag,Len;
mp4stream* Stream = p->Track;
if (!Stream) return;
ReadVersion(Reader(p));
Tag = Reader(p)->Read8(Reader(p));
Len = DescLen(p);
Reader(p)->Skip(Reader(p),2); //ID
if (Tag == 3)
Reader(p)->Skip(Reader(p),1); //priority
Tag = Reader(p)->Read8(Reader(p));
Len = DescLen(p);
if (Tag == 4) //config descr
{
int ObjTypeId = Reader(p)->Read8(Reader(p));
Reader(p)->Skip(Reader(p),12); //stream_type,buffer_size,max_bitrate,min_bitrate
Tag = Reader(p)->Read8(Reader(p));
Len = DescLen(p);
if (Tag == 5 && PacketFormatExtra(&Stream->Stream.Format,Len))
Reader(p)->Read(Reader(p),Stream->Stream.Format.Extra,Stream->Stream.Format.ExtraLength);
//override fourcc or format if neccessary
if (Stream->Stream.Format.Type == PACKET_VIDEO)
{
switch (ObjTypeId)
{
case MP4_MPEG1_VIDEO_TYPE:
case MP4_MPEG2_SIMPLE_VIDEO_TYPE:
case MP4_MPEG2_MAIN_VIDEO_TYPE:
case MP4_MPEG2_SNR_VIDEO_TYPE:
case MP4_MPEG2_SPATIAL_VIDEO_TYPE:
case MP4_MPEG2_HIGH_VIDEO_TYPE:
case MP4_MPEG2_442_VIDEO_TYPE:
Stream->Stream.Format.Format.Video.Pixel.FourCC = FOURCC('m','p','e','g');
break;
case MP4_MPEG4_VIDEO_TYPE:
Stream->Stream.Format.Format.Video.Pixel.FourCC = FOURCC('m','p','4','v');
break;
case MP4_JPEG_VIDEO_TYPE:
Stream->Stream.Format.Format.Video.Pixel.FourCC = FOURCC('j','p','e','g');
Stream->AllKey = 1;
break;
}
}
if (Stream->Stream.Format.Type == PACKET_AUDIO)
{
switch (ObjTypeId)
{
case MP4_MPEG1_AUDIO_TYPE:
case MP4_MPEG2_AUDIO_TYPE:
Stream->Stream.Format.Format.Audio.Format = AUDIOFMT_MP3;
break;
case MP4_MPEG2_AAC_MAIN_AUDIO_TYPE:
case MP4_MPEG2_AAC_LC_AUDIO_TYPE:
case MP4_MPEG2_AAC_SSR_AUDIO_TYPE:
case MP4_MPEG4_AUDIO_TYPE:
Stream->Stream.Format.Format.Audio.Format = AUDIOFMT_AAC;
break;
case MP4_PRIVATE_AUDIO_TYPE+1:
if (Stream->Stream.Format.ExtraLength>4 &&
*(uint32_t*)Stream->Stream.Format.Extra == FOURCC('Q','L','C','M'))
Stream->Stream.Format.Format.Audio.Format = AUDIOFMT_QCELP;
break;
}
}
}
}
static void SetText(mp4* p,uint32_t FourCC)
{
packetformat* Format = &p->Track->Stream.Format;
PacketFormatClear(Format);
Format->Type = PACKET_SUBTITLE;
Format->Format.Subtitle.FourCC = FourCC;
PacketFormatDefault(Format);
}
static void SetAudio(mp4* p,int AudioFormat,int SampleRate,int Bits,int Channels)
{
packetformat* Format = &p->Track->Stream.Format;
PacketFormatClear(Format);
Format->Type = PACKET_AUDIO;
Format->Format.Audio.Format = AudioFormat;
Format->Format.Audio.Flags = PCM_PACKET_BASED;
Format->Format.Audio.SampleRate = SampleRate;
Format->Format.Audio.Bits = Bits;
Format->Format.Audio.Channels = Channels;
PacketFormatDefault(Format);
}
static void ReadAudio(mp4* p,int AudioFormat,int Atom,filepos_t EndOfAtom,int FixedBits)
{
packetformat* Format = &p->Track->Stream.Format;
PacketFormatClear(Format);
Format->Type = PACKET_AUDIO;
Format->Format.Audio.Format = AudioFormat;
Format->Format.Audio.Channels = 1;
Format->Format.Audio.Flags = PCM_PACKET_BASED;
#ifdef IS_BIG_ENDIAN
if (Atom == FOURCC('s','o','w','t'))
Format->Format.Audio.Flags |= PCM_SWAPEDBYTES;
#else
if (Atom == FOURCC('t','w','o','s'))
Format->Format.Audio.Flags |= PCM_SWAPEDBYTES;
#endif
if (Atom == FOURCC('r','a','w',' ') || Atom == FOURCC('N','O','N','E'))
{
#ifndef IS_BIG_ENDIAN
Format->Format.Audio.Flags |= PCM_SWAPEDBYTES; // some digital cameras use 'raw ' with 16bit big endian
#endif
Format->Format.Audio.Bits = 8;
}
else
Format->Format.Audio.Bits = 16;
if (EndOfAtom - Reader(p)->FilePos >= 20)
{
int Ver = Reader(p)->ReadBE16(Reader(p));
Reader(p)->Skip(Reader(p),2+4); // revision, vendor
Format->Format.Audio.Channels = Reader(p)->ReadBE16(Reader(p)); // channel count
Format->Format.Audio.Bits = Reader(p)->ReadBE16(Reader(p)); // sample size
Reader(p)->Skip(Reader(p),2+2); // compression_id,packet_size
Format->Format.Audio.SampleRate = Reader(p)->ReadBE16(Reader(p)); // sample rate
Reader(p)->Skip(Reader(p),2); // reserved (part of 32bit sample rate?)
if (FixedBits < 0)
FixedBits = Format->Format.Audio.Bits;
if (Ver == 1)
{
p->Track->SamplesPerPacket = Reader(p)->ReadBE32(Reader(p));
Reader(p)->ReadBE32(Reader(p));
p->Track->BytesPerPacket = Reader(p)->ReadBE32(Reader(p));
Reader(p)->ReadBE32(Reader(p));
if (AudioFormat == AUDIOFMT_ADPCM_MS || AudioFormat == AUDIOFMT_ADPCM_IMA)
Format->Format.Audio.BlockAlign = p->Track->BytesPerPacket;
}
else
if (FixedBits)
{
p->Track->SamplesPerPacket = 1;
p->Track->BytesPerPacket = (FixedBits * Format->Format.Audio.Channels) >> 3;
}
ReadSubAtoms(p,EndOfAtom);
}
PacketFormatDefault(Format);
}
static void ReadVideo(mp4* p,int FourCC,filepos_t EndOfAtom)
{
int ColTable,BitCount,ColCount;
bool_t ColGray;
packetformat* Format = &p->Track->Stream.Format;
PacketFormatClear(Format);
Format->Type = PACKET_VIDEO;
Reader(p)->Skip(Reader(p),16); // reserved
Format->Format.Video.Pixel.FourCC = FourCC;
Format->Format.Video.Width = Reader(p)->ReadBE16(Reader(p));
Format->Format.Video.Height = Reader(p)->ReadBE16(Reader(p));
Format->Format.Video.Aspect = ASPECT_ONE; //todo: fix
if (FourCC == FOURCC('m','p','4','v'))
{
// use mpeg4 vol information instead
Format->Format.Video.Width = 0;
Format->Format.Video.Height = 0;
}
Reader(p)->Skip(Reader(p),14+32); // reserved,compressor
BitCount = Reader(p)->ReadBE16(Reader(p));
ColGray = (BitCount & 32)!=0;
BitCount &= 31;
ColCount = 1<<BitCount;
Format->Format.Video.Pixel.BitCount = BitCount;
ColTable = Reader(p)->ReadBE16(Reader(p));
if ((BitCount==2 || BitCount==4 || BitCount==8) &&
PacketFormatExtra(Format,sizeof(rgb)*ColCount))
{
rgb* Pal = Format->Format.Video.Pixel.Palette = (rgb*)Format->Extra;
if (ColGray)
{
int i;
for (i=0;i<ColCount;++i)
{
int v = 255 - (i*255)/(ColCount-1);
Pal[i].c.r = (uint8_t)v;
Pal[i].c.g = (uint8_t)v;
Pal[i].c.b = (uint8_t)v;
Pal[i].c.a = 0;
}
}
else
if (ColTable & 8)
{
// default palette
int i;
const rgbval_t* v = MovPal;
if (BitCount>2) v += 1<<2;
if (BitCount>4) v += 1<<4;
for (i=0;i<ColCount;++i)
Pal[i].v = v[i];
}
else
{
int Start,End;
Start = Reader(p)->ReadBE32(Reader(p));
Reader(p)->Skip(Reader(p),2); // count
End = Reader(p)->ReadBE16(Reader(p))+1;
if (End > ColCount)
End = ColCount;
for (;Start<End;++Start)
{
uint8_t v[4][2];
Reader(p)->Read(Reader(p),v,sizeof(v)); // 16bit a,r,g,b
Pal[Start].c.r = v[1][0];
Pal[Start].c.g = v[2][0];
Pal[Start].c.b = v[3][0];
Pal[Start].c.a = 0;
}
}
}
ReadSubAtoms(p,EndOfAtom);
PacketFormatDefault(Format);
}
static void ReadSTSD(mp4* p,filepos_t EndOfAtom)
{
mp4stream* Stream = p->Track;
int DescCount;
mp4atom Atom;
if (!Stream) return;
ReadVersion(Reader(p));
DescCount = Reader(p)->ReadBE32(Reader(p));
if (DescCount && ReadAtom(Reader(p),&Atom))
{
uint32_t FirstAtom = Atom.Atom;
Reader(p)->Skip(Reader(p),6+2); // reserved,reference index
Stream->Stream.Fragmented = 0;
// fourcc codes from libavformat
if (Atom.Atom == FOURCC('A','V','D','J'))
Atom.Atom = FOURCC('j','p','e','g');
switch (Atom.Atom)
{
case FOURCC('p','n','g',' '):
Atom.Atom = FOURCC('P','N','G','_');
case FOURCC('t','i','f','f'):
case FOURCC('j','p','e','g'):
case FOURCC('m','j','p','a'):
case FOURCC('m','j','p','b'):
Stream->AllKey = 1;
// no break;
case FOURCC('3','I','V','2'):
case FOURCC('3','I','V','1'):
case FOURCC('a','v','c','1'):
case FOURCC('S','V','Q','1'):
case FOURCC('S','V','Q','3'):
case FOURCC('m','p','4','v'):
case FOURCC('D','I','V','X'):
case FOURCC('V','P','3','1'):
case FOURCC('h','2','6','3'):
case FOURCC('s','2','6','3'):
case FOURCC('c','v','i','d'):
ReadVideo(p,Atom.Atom,Atom.EndOfAtom);
break;
// case FOURCC('O','g','g','V'):
// Stream->Stream.Fragmented = 1;
// ReadAudio(p,AUDIOFMT_VORBIS_MODE1,Atom.Atom,Atom.EndOfAtom,-1);
// break;
case FOURCC('s','o','w','t'):
case FOURCC('t','w','o','s'):
case FOURCC('r','a','w',' '):
case FOURCC('N','O','N','E'):
Stream->Stream.Fragmented = 1;
ReadAudio(p,AUDIOFMT_PCM,Atom.Atom,Atom.EndOfAtom,-1);
break;
case FOURCC('u','l','a','w'):
Stream->Stream.Fragmented = 1;
ReadAudio(p,AUDIOFMT_MULAW,Atom.Atom,Atom.EndOfAtom,8);
break;
case FOURCC('a','l','a','w'):
Stream->Stream.Fragmented = 1;
ReadAudio(p,AUDIOFMT_ALAW,Atom.Atom,Atom.EndOfAtom,8);
break;
case FOURCC('Q','D','M','2'):
Stream->Stream.Fragmented = 1;
ReadAudio(p,AUDIOFMT_QDESIGN2,Atom.Atom,Atom.EndOfAtom,0);
break;
case FOURCC('.','m','p','3'):
case 0x6D730055:
case 0x5500736D:
Stream->Stream.Fragmented = 1;
ReadAudio(p,AUDIOFMT_MP3,Atom.Atom,Atom.EndOfAtom,0);
break;
case 0x6D730002:
case 0x0200736D:
Stream->Stream.Fragmented = 1;
ReadAudio(p,AUDIOFMT_ADPCM_MS,Atom.Atom,Atom.EndOfAtom,0);
break;
case 0x6D730011:
case 0x1100736D:
Stream->Stream.Fragmented = 1;
ReadAudio(p,AUDIOFMT_ADPCM_IMA,Atom.Atom,Atom.EndOfAtom,0);
break;
case FOURCC('i','m','a','4'):
Stream->Stream.Fragmented = 1;
ReadAudio(p,AUDIOFMT_ADPCM_IMA_QT,Atom.Atom,Atom.EndOfAtom,0);
Stream->SamplesPerPacket = 64;
Stream->BytesPerPacket = 34*Stream->Stream.Format.Format.Audio.Channels;
break;
case FOURCC('m','p','4','a'):
ReadAudio(p,AUDIOFMT_AAC,Atom.Atom,Atom.EndOfAtom,0);
break;
case FOURCC('s','a','m','r'):
SetAudio(p,AUDIOFMT_AMR_NB,8000,16,1);
break;
case FOURCC('s','a','w','b'):
SetAudio(p,AUDIOFMT_AMR_WB,16000,16,1);
break;
case FOURCC('t','e','x','t'):
SetText(p,UpperFourCC(Atom.Atom));
break;
case FOURCC('t','x','3','g'):
SetText(p,UpperFourCC(Atom.Atom));
break;
}
while (--DescCount>0)
{
Reader(p)->Seek(Reader(p),Atom.EndOfAtom,SEEK_SET);
ReadAtom(Reader(p),&Atom);
if (Atom.Atom != FirstAtom)
{
if (!p->ErrorShowed)
{
p->ErrorShowed = 1;
ShowError(p->Format.Format.Class,MP4_ID,MP4_MULTIPLE_FORMAT);
}
Stream->Stream.Format.Type = PACKET_NONE;
break;
}
}
}
if (Stream->Stream.Format.Type == PACKET_NONE)
{
Format_RemoveStream(&p->Format);
p->Track = NULL;
}
}
static void ReadMVHD(mp4* p,filepos_t EndOfAtom)
{
int Duration;
ReadVersion(Reader(p));
Reader(p)->ReadBE32(Reader(p)); // creation time
Reader(p)->ReadBE32(Reader(p)); // modification time
p->TimeScale = Reader(p)->ReadBE32(Reader(p)); // timescale
Duration = Reader(p)->ReadBE32(Reader(p)); // duration
if (Duration>0)
p->Format.Duration = Scale(Duration,TICKSPERSEC,p->TimeScale);
}
static void ReadMDHD(mp4* p,filepos_t EndOfAtom)
{
int Duration = -1;
if (!p->Track) return;
switch (ReadVersion(Reader(p)))
{
case 0:
Reader(p)->ReadBE32(Reader(p)); // creation time
Reader(p)->ReadBE32(Reader(p)); // modification time
p->Track->TimeScale = Reader(p)->ReadBE32(Reader(p)); // timescale
Duration = Reader(p)->ReadBE32(Reader(p)); // duration
break;
case 1:
Reader(p)->ReadBE64(Reader(p)); // creation time
Reader(p)->ReadBE64(Reader(p)); // modification time
p->Track->TimeScale = Reader(p)->ReadBE32(Reader(p)); // timescale
Duration = (int)Reader(p)->ReadBE64(Reader(p)); // duration
break;
}
if (Duration>0)
{
Duration = Scale(Duration,TICKSPERSEC,p->Track->TimeScale);
if (p->Format.Duration < Duration)
p->Format.Duration = Duration;
}
p->Track->Duration = Duration;
}
static void ReadCMOV(mp4* p,filepos_t EndOfAtom)
{
ReadSubAtoms(p,EndOfAtom);
p->CompressType = 0;
}
static void ReadDCOM(mp4* p,filepos_t EndOfAtom)
{
p->CompressType = 0;
Reader(p)->Read(Reader(p),&p->CompressType,sizeof(p->CompressType));
}
static void ReadCMVD(mp4* p,filepos_t EndOfAtom)
{
if (p->CompressType == FOURCC('z','l','i','b'))
{
uint32_t DstLen = Reader(p)->ReadBE32(Reader(p));
int SrcLen = EndOfAtom - Reader(p)->FilePos;
uint8_t* Src = (uint8_t*)malloc(SrcLen);
uint8_t* Dst = (uint8_t*)malloc(DstLen);
if (Src && Dst && Reader(p)->Read(Reader(p),Src,SrcLen)==SrcLen &&
uncompress(Dst,&DstLen,Src,SrcLen)==Z_OK)
{
format_buffer Buffer;
format_reader* Reader = p->Format.Reader;
format_reader Backup = *Reader;
Buffer.Block.Id = 0;
Buffer.Block.Ptr = Dst;
Buffer.Length = DstLen;
Reader->BufferFirst = NULL;
Reader->BufferLast = NULL;
Reader->NoMoreInput = 1;
Reader->ReadPos = 0;
Reader->ReadLen = DstLen;
Reader->FilePos = 0;
Reader->Input = NULL;
Reader->InputBuffer = NULL;
Reader->ReadBuffer = &Buffer;
Reader->Format = &p->Format;
Reader->BufferAvailable = 0;
ReadSubAtoms(p,DstLen);
*Reader = Backup;
}
free(Src);
free(Dst);
}
}
static void AddMeta(mp4* p,const tchar_t* Meta,int Size)
{
pin* Comment = &p->Format.Comment;
format_stream* Stream = (format_stream*)p->Track;
if (Stream)
Comment = &Stream->Comment;
if (Comment->Node)
Comment->Node->Set(Comment->Node,Comment->No,Meta,Size);
}
static void ReadMetaString(mp4* p,filepos_t EndOfAtom)
{
tchar_t Meta[512];
char UTF8[512];
int Len = EndOfAtom - Reader(p)->FilePos;
if (Len>=512) Len=511;
Len = Reader(p)->Read(Reader(p),UTF8,Len);
if (Len>0)
{
UTF8[Len] = 0;
tcscpy_s(Meta,TSIZEOF(Meta),p->Meta);
UTF8ToTcs(Meta+tcslen(p->Meta),TSIZEOF(Meta)-tcslen(p->Meta),UTF8);
AddMeta(p,Meta,sizeof(Meta));
}
}
static void ReadRDRF(mp4* p,filepos_t EndOfAtom)
{
if (p->RMRAFound)
{
//redirect
if (Reader(p)->ReadBE32(Reader(p))==0)
{
int Size;
uint32_t Type = 0;
Reader(p)->Read(Reader(p),&Type,sizeof(Type));
Size = Reader(p)->ReadBE32(Reader(p));
if (Type == FOURCC('u','r','l',' '))
{
tcscpy_s(p->Meta,TSIZEOF(p->Meta),T("REDIRECT="));
ReadMetaString(p,min(EndOfAtom,Reader(p)->FilePos + Size));
p->Meta[0] = 0;
}
}
}
}
static void ReadDATA(mp4* p,filepos_t EndOfAtom)
{
ReadVersion(Reader(p));
Reader(p)->Skip(Reader(p),4); //reserved;
if (tcscmp(p->Meta,T("COVER="))==0)
{
tchar_t Meta[256];
stprintf_s(Meta,TSIZEOF(Meta),T("%s:%d:%d:"),p->Meta,Reader(p)->FilePos,EndOfAtom-Reader(p)->FilePos);
AddMeta(p,Meta,sizeof(Meta));
}
else
if (tcscmp(p->Meta,T("GENRE="))==0)
{
tchar_t Meta[256];
tcscpy_s(Meta,TSIZEOF(Meta),p->Meta);
if (ID3v1_Genre(Reader(p)->ReadBE16(Reader(p))-1,Meta+tcslen(Meta),256-tcslen(Meta)))
AddMeta(p,Meta,sizeof(Meta));
}
else
if (p->Meta[0])
ReadMetaString(p,EndOfAtom);
}
static void ReadNeroChapter(mp4* p,filepos_t EndOfAtom)
{
int Count,i,Len;
Reader(p)->Read8(Reader(p)); // version?
Reader(p)->Skip(Reader(p),4); // ???
Count = Reader(p)->ReadBE32(Reader(p));
for (i=0;i<Count;++i)
{
int64_t Time = Reader(p)->ReadBE64(Reader(p));
stprintf_s(p->Meta,TSIZEOF(p->Meta),T("CHAPTER%02dNAME="),i+1);
Len = Reader(p)->Read8(Reader(p));
ReadMetaString(p,Reader(p)->FilePos + Len);
BuildChapter(p->Meta,TSIZEOF(p->Meta),i+1,Time,10000);
AddMeta(p,p->Meta,sizeof(p->Meta));
}
}
static void ReadNAME(mp4* p,filepos_t EndOfAtom)
{
ReadVersion(Reader(p));
if (p->Meta[0])
ReadMetaString(p,EndOfAtom);
else
{
char UTF8[512];
int Len = EndOfAtom - Reader(p)->FilePos;
if (Len>510) Len=510;
Len = Reader(p)->Read(Reader(p),UTF8,Len);
if (Len>0)
{
UTF8[Len] = '=';
UTF8[Len+1] = 0;
UTF8ToTcs(p->Meta,TSIZEOF(p->Meta),UTF8);
}
}
}
static void ReadMeta(mp4* p,filepos_t EndOfAtom, const tchar_t* Meta)
{
mp4atom Atom;
int Pos = Reader(p)->FilePos;
tcscpy_s(p->Meta,TSIZEOF(p->Meta),Meta);
ReadAtom(Reader(p),&Atom);
Reader(p)->Seek(Reader(p),Pos,SEEK_SET);
if (Atom.EndOfAtom > EndOfAtom)
ReadNAME(p,EndOfAtom);
else
ReadSubAtoms(p,EndOfAtom);
p->Meta[0] = 0;
}
static void ReadMetaCover(mp4* p,filepos_t EndOfAtom) { ReadMeta(p,EndOfAtom,T("COVER=")); }
static void ReadMetaName(mp4* p,filepos_t EndOfAtom) { ReadMeta(p,EndOfAtom,T("TITLE=")); }
static void ReadMetaArtist(mp4* p,filepos_t EndOfAtom) { ReadMeta(p,EndOfAtom,T("ARTIST=")); }
static void ReadMetaWriter(mp4* p,filepos_t EndOfAtom) { ReadMeta(p,EndOfAtom,T("WRITER=")); }
static void ReadMetaAlbum(mp4* p,filepos_t EndOfAtom) { ReadMeta(p,EndOfAtom,T("ALBUM=")); }
static void ReadMetaDate(mp4* p,filepos_t EndOfAtom) { ReadMeta(p,EndOfAtom,T("DATE=")); }
static void ReadMetaTool(mp4* p,filepos_t EndOfAtom) { ReadMeta(p,EndOfAtom,T("TOOL=")); }
static void ReadMetaComment(mp4* p,filepos_t EndOfAtom) { ReadMeta(p,EndOfAtom,T("COMMENT=")); }
static void ReadMetaGenre(mp4* p,filepos_t EndOfAtom) { ReadMeta(p,EndOfAtom,T("GENRE=")); }
static const mp4atomtable AtomTable[] =
{
{ FOURCC('f','t','y','p'), ReadFTYP },
{ FOURCC('m','o','o','v'), ReadMOOV },
{ FOURCC('t','r','a','k'), ReadTRAK },
{ FOURCC('m','v','h','d'), ReadMVHD },
{ FOURCC('m','d','h','d'), ReadMDHD },
{ FOURCC('s','t','b','l'), ReadSubAtoms },
{ FOURCC('m','i','n','f'), ReadSubAtoms },
{ FOURCC('e','d','t','s'), ReadSubAtoms },
{ FOURCC('m','d','i','a'), ReadSubAtoms },
{ FOURCC('u','d','t','a'), ReadSubAtoms },
{ FOURCC('i','l','s','t'), ReadSubAtoms },
{ FOURCC('w','a','v','e'), ReadSubAtoms },
{ FOURCC('-','-','-','-'), ReadSubAtoms },
{ FOURCC('r','m','d','a'), ReadSubAtoms },
{ FOURCC('r','m','r','a'), ReadRMRA },
{ FOURCC('r','d','r','f'), ReadRDRF },
{ FOURCC('m','e','t','a'), ReadMETA },
{ FOURCC('s','t','s','d'), ReadSTSD },
{ FOURCC('s','t','s','s'), ReadSTSS },
{ FOURCC('s','t','s','c'), ReadSTSC },
{ FOURCC('s','t','c','o'), ReadSTCO },
{ FOURCC('c','t','t','s'), ReadCTTS },
{ FOURCC('s','t','s','z'), ReadSTSZ },
{ FOURCC('s','t','t','s'), ReadSTTS },
{ FOURCC('c','m','o','v'), ReadCMOV },
{ FOURCC('d','c','o','m'), ReadDCOM },
{ FOURCC('c','m','v','d'), ReadCMVD },
{ FOURCC('e','s','d','s'), ReadESDS },
{ FOURCC('S','M','I',' '), ReadExtra },
{ FOURCC('a','v','c','C'), ReadExtra },
{ FOURCC('d','a','t','a'), ReadDATA },
{ FOURCC('n','a','m','e'), ReadNAME },
{ FOURCC('c','h','p','l'), ReadNeroChapter },
{ FOURCC('\251','n','a','m'), ReadMetaName },
{ FOURCC('\251','A','R','T'), ReadMetaArtist },
{ FOURCC('\251','w','r','t'), ReadMetaWriter },
{ FOURCC('\251','a','l','b'), ReadMetaAlbum },
{ FOURCC('\251','d','a','y'), ReadMetaDate },
{ FOURCC('\251','t','o','o'), ReadMetaTool },
{ FOURCC('\251','c','m','t'), ReadMetaComment },
{ FOURCC('\251','g','e','n'), ReadMetaGenre },
{ FOURCC('c','o','v','r'), ReadMetaCover },
{ FOURCC('g','n','r','e'), ReadMetaGenre },
{ 0, NULL }
};
static void ReadSubAtoms(mp4* p,filepos_t EndOfAtom)
{
mp4atom Atom;
const mp4atomtable* i;
while (Reader(p)->FilePos < EndOfAtom - 8)
{
if (!ReadAtom(Reader(p),&Atom))
break;
if (p->MOOVFound && Atom.Atom == FOURCC('m','d','a','t'))
break;
for (i=AtomTable;i->Atom;++i)
if (i->Atom == Atom.Atom)
{
i->Func(p,Atom.EndOfAtom);
break;
}
Reader(p)->Seek(Reader(p),Atom.EndOfAtom,SEEK_SET);
}
p->Meta[0] = 0;
}
static mp4stream* FindNextChunk(mp4* p, format_reader* Reader)
{
mp4stream* Stream = NULL;
int Min = MAX_INT;
int No;
for (No=0;No<p->Format.StreamCount;++No)
{
mp4stream* s = (mp4stream*)p->Format.Streams[No];
if (s->Stream.Pin.Node && s->Stream.Reader == Reader && Min > *s->Pos.STCOPos)
{
Min = *s->Pos.STCOPos;
Stream = s;
}
}
if (Stream)
{
if (Reader->Seek(Reader,Min,SEEK_SET) == ERR_NONE)
{
Reader->Current = &Stream->Stream;
NextChunk(Stream,&Stream->Pos);
}
else
Stream = NULL;
}
return Stream;
}
static void AddTextChapters(mp4* p,mp4stream* s)
{
format_reader* Reader = p->Format.Reader;
mp4streampos Pos;
int No = 1;
Head(s,&Pos);
for (;;)
{
filepos_t FilePos = *Pos.STCOPos;
if (FilePos == MAX_INT)
break;
NextChunk(s,&Pos);
while (Pos.SamplesLeft)
{
if (Reader->Seek(Reader,FilePos,SEEK_SET) == ERR_NONE)
{
int Len = Reader->ReadBE16(Reader);
if (Len)
{
stprintf_s(p->Meta,TSIZEOF(p->Meta),T("CHAPTER%02dNAME="),No);
ReadMetaString(p,Reader->FilePos+Len);
BuildChapter(p->Meta,TSIZEOF(p->Meta),No,(int64_t)Pos.CurrentTime*1000,s->TimeScale);
AddMeta(p,p->Meta,sizeof(p->Meta));
++No;
}
}
FilePos += NextSample(s,&Pos);
}
}
}
static int Init(mp4* p)
{
int No;
p->MOOVFound = 0;
p->Type = FOURCC('m','o','o','v'); //quicktime .mov
p->TimeScale = 0;
p->ErrorShowed = 0;
p->Format.HeaderLoaded = 1;
p->Meta[0] = 0;
ReadSubAtoms(p,MAX_INT);
if (!p->MOOVFound)
return ERR_INVALID_DATA;
if (p->Format.Comment.Node)
for (No=0;No<p->Format.StreamCount;++No)
{
mp4stream* s = (mp4stream*)p->Format.Streams[No];
if (s->Stream.Format.Type == PACKET_SUBTITLE &&
s->Stream.Format.Format.Subtitle.FourCC == FOURCC('T','E','X','T') && GetFrames(s)<100)
AddTextChapters(p,s);
}
for (No=0;No<p->Format.StreamCount;++No)
{
mp4stream* s = (mp4stream*)p->Format.Streams[No];
format_reader* Reader = Format_FindReader(&p->Format,s->OffsetMin,s->OffsetMax);
if (!Reader)
break;
s->Stream.Reader = Reader;
}
for (No=0;No<MAXREADER;++No)
FindNextChunk(p,p->Format.Reader+No);
return ERR_NONE;
}
static int ReadPacket(mp4* p, format_reader* Reader, format_packet* Packet)
{
mp4stream* Stream = (mp4stream*) Reader->Current;
if (!Stream)
{
Stream = FindNextChunk(p,Reader);
if (!Stream)
return ERR_END_OF_FILE;
}
Packet->Stream = &Stream->Stream;
Packet->RefTime = Scale(Stream->Pos.CurrentTime,TICKSPERSEC,Stream->TimeScale);
if (Stream->Pos.STSSValue >= 0)
{
Packet->Key = Stream->Pos.SampleNo == Stream->Pos.STSSValue;
if (Packet->Key)
NextKey(&Stream->Pos);
}
Packet->Data = Reader->ReadAsRef(Reader,NextSample(Stream,&Stream->Pos));
if (Stream->Pos.SamplesLeft==0)
Reader->Current = NULL;
return ERR_NONE;
}
static bool_t GetNeedKey(mp4stream* Stream)
{
return (!ARRAYEMPTY(Stream->STSS) || Stream->AllKey) && Stream->Stream.Format.Type == PACKET_VIDEO;
}
static int SeekReader(mp4* p, format_reader* Reader, tick_t* DstTime, filepos_t DstPos, bool_t PrevKey)
{
mp4stream* Stream = (mp4stream*) Format_DefSyncStream(&p->Format,Reader);
if (Stream && Reader->Input)
{
mp4streampos Last;
mp4streampos Pos;
bool_t NeedKey;
tick_t SyncTime;
int DstMediaTime,No;
filepos_t FilePos;
filepos_t LastFilePos = -1;
DstMediaTime = -1;
if (*DstTime >= 0)
DstMediaTime = Scale(*DstTime,Stream->TimeScale,TICKSPERSEC);
NeedKey = GetNeedKey(Stream);
if (!NeedKey)
PrevKey = 0;
else
if (Stream->AllKey) // SyncTime will be independent anyway. we always need current chapter image
PrevKey = 1;
if (PrevKey && DstMediaTime >= 0)
DstMediaTime += 1+Stream->TimeScale/256; // seek to same position even if there is rounding error
Head(Stream,&Last);
Head(Stream,&Pos);
for (;;)
{
FilePos = *Pos.STCOPos;
if (FilePos == MAX_INT)
break;
NextChunk(Stream,&Pos);
while (Pos.SamplesLeft)
{
bool_t Key = Pos.SampleNo == Pos.STSSValue;
if (DstMediaTime >= 0 && Pos.CurrentTime >= DstMediaTime && (!NeedKey || Key))
{
if (PrevKey && Pos.CurrentTime != DstMediaTime && LastFilePos>=0)
{
FilePos = LastFilePos;
Pos = Last;
}
goto found;
}
if (DstPos >= 0 && FilePos >= DstPos)
goto found;
if (Key || Pos.STSSValue<0)
{
LastFilePos = FilePos;
Last = Pos;
}
if (Key) NextKey(&Pos);
FilePos += NextSample(Stream,&Pos);
}
}
if (LastFilePos>=0)
{
FilePos = LastFilePos;
Pos = Last;
}
found:
if (FilePos==MAX_INT || Reader->Seek(Reader,FilePos,SEEK_SET) != ERR_NONE)
return ERR_NOT_SUPPORTED;
Stream->Stream.Reader->Current = Pos.SamplesLeft ? &Stream->Stream:NULL;
Stream->Pos = Pos;
SyncTime = Scale(Pos.CurrentTime,TICKSPERSEC,Stream->TimeScale);
if (*DstTime >= 0 && (!Stream->AllKey || (abs(*DstTime-SyncTime)<TICKSPERSEC/16))) // use updated time with other Readers
*DstTime = SyncTime;
// set current positions for other streams
for (No=0;No<p->Format.StreamCount;++No)
{
mp4stream* s = (mp4stream*) p->Format.Streams[No];
if (s->Stream.Reader==Reader && s != Stream)
{
Head(s,&s->Pos);
for (;;)
{
if (*s->Pos.STCOPos >= FilePos)
break;
NextChunk(s,&s->Pos);
while (s->Pos.SamplesLeft)
{
if (s->Pos.SampleNo == s->Pos.STSSValue)
NextKey(&s->Pos);
NextSample(s,&s->Pos);
}
}
}
}
}
return ERR_NONE;
}
static int Seek(mp4* p, tick_t DstTime, filepos_t DstPos, bool_t PrevKey)
{
int Result;
int Skip = -1;
int No;
for (No=0;No<MAXREADER;++No)
{
mp4stream* Stream = (mp4stream*) Format_DefSyncStream(&p->Format,&p->Format.Reader[No]);
if (Stream && GetNeedKey(Stream))
{
Skip = No;
Result = SeekReader(p,&p->Format.Reader[No],&DstTime,DstPos,PrevKey);
if (Result != ERR_NONE)
return Result;
if (Stream->AllKey && DstTime >= 0)
p->Format.SyncTime = DstTime;
break;
}
}
if (Skip<0)
PrevKey = 0;
for (No=0;No<MAXREADER;++No)
if (No != Skip)
{
Result = SeekReader(p,&p->Format.Reader[No],&DstTime,DstPos,PrevKey);
if (Result != ERR_NONE)
return Result;
}
Format_AfterSeek(&p->Format);
return ERR_NONE;
}
static void FreeStream(mp4* p, mp4stream* Stream)
{
ArrayClear(&Stream->STSS);
ArrayClear(&Stream->STSZ);
ArrayClear(&Stream->STTS);
ArrayClear(&Stream->STSC);
ArrayClear(&Stream->STCO);
}
static int Create(mp4* p)
{
p->Format.Init = (fmtfunc)Init;
p->Format.Seek = (fmtseek)Seek;
p->Format.ReadPacket = (fmtreadpacket)ReadPacket;
p->Format.FreeStream = (fmtstream)FreeStream;
return ERR_NONE;
}
static const nodedef MP4 =
{
sizeof(mp4),
MP4_ID,
FORMATBASE_CLASS,
PRI_DEFAULT,
(nodecreate)Create,
};
void MP4_Init()
{
NodeRegisterClass(&MP4);
}
void MP4_Done()
{
NodeUnRegisterClass(MP4_ID);
}