gps/GPSResources/tcpmp/splitter/avi.c

1624 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: avi.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 "avi.h"
#define INDEXSTEP 512
#define AVI_INDEX_OF_INDEXES 0x00
#define AVI_INDEX_OF_CHUNKS 0x01
#define AVI_INDEX_KEY 0x80000000
typedef struct aviindex
{
int32_t Id;
int8_t Flags[4];
int32_t Pos;
int32_t Length;
} aviindex;
typedef struct avisuperindex
{
int Offset;
int Size;
int StartTime;
int EndTime;
int LastData;
} avisuperindex;
typedef struct avistdindex
{
uint32_t FourCC;
int Size;
int16_t LongsPerEntry;
int8_t IndexSubType;
int8_t IndexType;
int Count;
int ChunkId;
int OffsetLow;
int OffsetHigh;
int Reserved;
} avistdindex;
typedef struct avistream
{
format_stream Stream;
int* Index; // mediatime
bool_t ByteRate;
int RefStart;
int MediaTime;
int NewMediaTime; // after seek (if seek successed)
fraction64 MediaRate; // byterate or packetrate
int SampleSize;
int MaxPacket;
int Length;
int SuperIndexCount;
avisuperindex* SuperIndex; //opendml index
int PrevKey;
int PrevMediaTime;
int PrevKeyBuffer;
filepos_t OffsetMin;
filepos_t OffsetMax;
int* ZeroChunkPos;
array ZeroChunk;
int LastPos;
} avistream;
typedef struct avi
{
format_base Format;
int FramePeriod;
int ByteRate;
bool_t ErrorIndex;
bool_t UnLimited;
bool_t CheckForZero;
bool_t FoundStdIndex;
int DataPos;
int DataEnd;
// stream header and format loading
int HeadType;
avistream* LastStream;
int VideoHandler;
int VideoFrames;
fraction VideoFPS;
int VideoStart;
fraction AudioRate;
int AudioLength;
int AudioStart;
int AudioSampleSize;
int IndexAdjustPos;
int IndexCount;
int IndexPos;
int IndexReaded;
int IndexBufferNo;
block IndexBuffer;
char NameChar[256];
int FoundTotal;
int FoundAtEnd[8];
int FoundLastPos[8];
} avi;
#define ALIGN2(x) (((x)+1)&~1)
#define ISDIGIT(x) ((x)>='0' && (x)<='9')
#define STREAMNO(x) ((((x) & 255)-'0')*10 + ((((x) >> 8) & 255)-'0'))
#define ISDATA(x) (ISDIGIT((x) & 255) && ISDIGIT(((x) >> 8) & 255))
#define ISIDX(x) (ISDIGIT(((x) >> 16) & 255) && ISDIGIT(((x) >> 24) & 255) && ((x) & 255)=='i' && (((x) >> 8) & 255)=='x')
#define IDXNO(x) (((((x) >> 16) & 255)-'0')*10 + ((((x) >> 24) & 255)-'0'))
static void Done(avi* p)
{
FreeBlock(&p->IndexBuffer);
}
#define CHECKINDEX 16384
static NOINLINE int Load32LE(const uint8_t* i) { return LOAD32LE(i); }
static void PreCheckIndexODML(avi* p,avistream* Stream)
{
int k;
array Array = {NULL};
avistdindex StdIndex;
stream* Input = p->Format.Reader->Input;
filepos_t Save = Input->Seek(Input,0,SEEK_CUR);
if (Save>=0 && Input->Seek(Input,Stream->SuperIndex[0].Offset,SEEK_SET)>=0)
{
if (Input->Read(Input,&StdIndex,sizeof(StdIndex)) == sizeof(StdIndex) &&
StdIndex.IndexType == AVI_INDEX_OF_CHUNKS &&
StdIndex.LongsPerEntry == 2)
{
if (StdIndex.Count > 1024)
StdIndex.Count = 1024;
if (ArrayAppend(&Array,NULL,8*StdIndex.Count,1024))
{
if (Input->Read(Input,ARRAYBEGIN(Array,int32_t),8*StdIndex.Count) == 8*StdIndex.Count)
{
for (k=0;k<StdIndex.Count;++k)
if ((ARRAYBEGIN(Array,int32_t)[k*2+1] & ~AVI_INDEX_KEY)==0)
{
p->CheckForZero = 1;
break;
}
}
ArrayClear(&Array);
}
}
Input->Seek(Input,Save,SEEK_SET);
}
}
static void PreCheckIndex(avi* p)
{
block Buffer;
if (AllocBlock(CHECKINDEX,&Buffer,0,HEAP_ANY))
{
format_reader* Reader = p->Format.Reader;
filepos_t Save = Reader->Input->Seek(Reader->Input,0,SEEK_CUR);
if (Save>=0 && Reader->Input->Seek(Reader->Input,-CHECKINDEX,SEEK_END)>=0)
{
if (Reader->Input->ReadBlock(Reader->Input,&Buffer,0,CHECKINDEX)==CHECKINDEX)
{
int Type = 0;
int LastNo;
int No = 24;
const uint8_t* i = Buffer.Ptr+CHECKINDEX-12;
while (--i>=Buffer.Ptr+16)
{
if (p->NameChar[*i])
{
Type |= *i << No;
No -= 8;
if (No<0)
{
if (Type == FOURCCLE('J','U','N','K') && ISDATA(Load32LE(i-16)))
{
i -= 16;
p->FoundStdIndex = 1;
break;
}
if (ISDATA(Type) && (Load32LE(i+4) & ~0x10)==0)
{
p->FoundStdIndex = 1;
break;
}
Type <<= 8;
No += 8;
}
}
else
{
Type = 0;
No = 24;
}
}
LastNo = -1;
for (;i>=Buffer.Ptr+16;i-=16)
{
if (Load32LE(i-8) == FOURCCLE('i','d','x','1'))
break;
Type = Load32LE(i);
if (ISDATA(Type))
{
int Pos = Load32LE(i+8);
int No = STREAMNO(Type);
if (No >= 0 && No < 8)
{
if (No != LastNo)
{
if (LastNo>=0)
++p->FoundAtEnd[LastNo];
LastNo = No;
}
if (p->FoundLastPos[No] == Pos)
p->CheckForZero = 1;
p->FoundLastPos[No] = Pos;
++p->FoundTotal;
}
if (Load32LE(i+12)==0 && Pos==Load32LE(i-16+8))
{
p->CheckForZero = 1;
break;
}
}
}
if (LastNo>=0)
++p->FoundAtEnd[LastNo];
}
Reader->Input->Seek(Reader->Input,Save,SEEK_SET);
}
FreeBlock(&Buffer);
}
}
static int Init(avi* p)
{
int DataLength;
int Type;
format_reader* Reader = p->Format.Reader;
if (!AllocBlock(sizeof(aviindex)*INDEXSTEP,&p->IndexBuffer,0,HEAP_ANY))
return ERR_OUT_OF_MEMORY;
memset(p->FoundAtEnd,0,sizeof(p->FoundAtEnd));
memset(p->FoundLastPos,0,sizeof(p->FoundLastPos));
p->LastStream = NULL;
p->FoundTotal = 0;
p->FoundStdIndex = 0;
p->CheckForZero = 0;
p->UnLimited = 1;
p->IndexAdjustPos = 4;
p->IndexPos = -1;
p->IndexCount = 0;
p->IndexReaded = 0;
p->IndexBufferNo = -1;
p->DataPos = -1;
p->DataEnd = -1;
p->ErrorIndex = 0;
p->AudioLength = 0;
memset(p->NameChar,0,sizeof(p->NameChar));
memset(p->NameChar+'a',1,'z'-'a');
memset(p->NameChar+'A',1,'Z'-'A');
memset(p->NameChar+'0',1,'9'-'0');
p->NameChar['_']=1;
p->NameChar[' ']=1;
Type = Reader->ReadLE32(Reader);
DataLength = Reader->ReadLE32(Reader);
if (Type != FOURCCLE('R','I','F','F'))
return ERR_INVALID_DATA;
if (DataLength > 256 && (p->Format.FileSize < 0 || p->Format.FileSize > DataLength + 8))
p->Format.FileSize = DataLength + 8;
Reader->ReadLE32(Reader); // 'AVI ' or 'AVIX'
PreCheckIndex(p);
return ERR_NONE;
}
static INLINE int ChunkTime(avistream* Stream, int DataLength)
{
if (Stream->ByteRate)
return DataLength; //byterate
// packetrate
if (DataLength > Stream->MaxPacket)
return (DataLength + Stream->MaxPacket - 1) / Stream->MaxPacket;
return 1;
}
static void UpdateAviFrameRate(avi* p)
{
if (QueryAdvanced(ADVANCED_AVIFRAMERATE) && p->AudioLength && p->VideoFrames)
{
int i;
for (i=0;i<p->Format.StreamCount;++i)
{
avistream* Stream = (avistream*) p->Format.Streams[i];
if (Stream->Stream.Format.Type == PACKET_VIDEO && Stream->Length)
{
Stream->MediaRate.Num = Scale64(p->AudioLength,(int64_t)p->AudioRate.Den*TICKSPERSEC,p->AudioRate.Num);
Stream->MediaRate.Den = p->VideoFrames;
}
}
}
}
static bool_t SubtitleTiming(format_packet* Packet,format_reader* Reader,int Length)
{
int Hour0,Hour1,Min0,Min1,Sec0,Sec1,MSec0,MSec1;
int i;
tchar_t s[64];
for (i=0;i<63 && Length>0;++i)
{
int ch = Reader->Read8(Reader);
--Length;
if (ch<0 || ch==']') break;
s[i] = (tchar_t)ch;
}
s[i] = 0;
if (stscanf(s,T("[%d:%d:%d.%d-%d:%d:%d.%d"),&Hour0,&Min0,&Sec0,&MSec0,&Hour1,&Min1,&Sec1,&MSec1)!=8)
{
Reader->Skip(Reader,Length);
return 0;
}
Packet->RefTime = Scale(((Hour0*60+Min0)*60+Sec0)*1000+MSec0,TICKSPERSEC,1000);
Packet->EndRefTime = Scale(((Hour1*60+Min1)*60+Sec1)*1000+MSec1,TICKSPERSEC,1000);
Packet->Data = Reader->ReadAsRef(Reader,Length);
return 1;
}
static void SetMediaTime(avistream* p,int Time)
{
p->MediaTime = Time;
p->ZeroChunkPos = ARRAYBEGIN(p->ZeroChunk,int);
if (p->ZeroChunkPos)
while (*p->ZeroChunkPos < Time)
++p->ZeroChunkPos;
}
static void ReadMeta(avi* p, format_reader* Reader, int* DataLength)
{
pin* Comment = &p->LastStream->Stream.Comment;
tchar_t Meta[512];
char UTF8[512];
int Len = *DataLength;
if (Len>=512) Len=511;
Len = Reader->Read(Reader,UTF8,Len);
if (Len>0)
{
*DataLength -= Len;
UTF8[Len] = 0;
tcscpy_s(Meta,TSIZEOF(Meta),T("LANGUAGE="));
UTF8ToTcs(Meta+tcslen(Meta),TSIZEOF(Meta)-tcslen(Meta),UTF8);
if (Comment->Node)
Comment->Node->Set(Comment->Node,Comment->No,Meta,sizeof(Meta));
}
}
static void CheckIndex(avi* p, format_reader* Reader);
static int ReadPacket(avi* p, format_reader* Reader, format_packet* Packet)
{
avistream* Stream;
int No,Ch,StartPos;
int SubType,Type,DataLength;
tick_t Duration;
int LongsPerEntry;
int IndexType;
int Count;
int BaseOffset;
// find a valid chunk header first
//DEBUG_MSG1(DEBUG_FORMAT,T("AVI Packet %08x"),Reader->FilePos);
StartPos = Reader->FilePos;
if (!p->UnLimited && p->DataEnd >= 0 && Reader->FilePos >= p->DataEnd)
return ERR_END_OF_FILE;
do
{
bool_t PossibleAlign = Reader->FilePos & 1;
Type = 0;
No = 0;
while (No<32)
{
Ch = Reader->Read8(Reader);
if (Ch>=0 && p->NameChar[Ch])
{
Type |= Ch << No;
No += 8;
if (No==32 && PossibleAlign)
{
int Stream = STREAMNO(Type);
if (!ISDATA(Type) || ISDIGIT((Type>>16) & 255) || ISDIGIT((Type>>24) & 255) ||
Stream <0 || Stream>=p->Format.StreamCount)
{
No -= 8;
Type >>= 8;
}
}
}
else
{
if (!PossibleAlign)
{
Reader->OutOfSync = 1;
if ((Reader->FilePos-StartPos)>=16384)
return ERR_DATA_NOT_FOUND; // this will force the process loop to stop, so input thread can be woken up
if (Reader->Eof(Reader))
return ERR_END_OF_FILE;
}
No = 0;
Type = 0;
PossibleAlign = 0;
}
}
if (!p->UnLimited && p->DataEnd >= 0 && Reader->FilePos >= p->DataEnd)
return ERR_END_OF_FILE;
DataLength = Reader->ReadLE32(Reader);
if (DataLength<0 || (Reader->FilePos+DataLength<0) || (p->DataPos>=0 && p->Format.FileSize>=0 && Reader->FilePos+DataLength>p->Format.FileSize))
{
DataLength = -1;
Reader->OutOfSync = 1;
continue;
}
if (ISDATA(Type))
{
No = STREAMNO(Type);
if (No >= 0 && No < p->Format.StreamCount)
{
//DEBUG_MSG3(-1,"AVI Packet stream:%d pos:%08x length:%d",No,Reader->FilePos,DataLength);
Stream = (avistream*) p->Format.Streams[No];
if (Stream->Stream.Reader != Reader)
break;
if (Reader->OutOfSync)
{
if (p->Format.ProcessTime >= 0)
{
int i;
tick_t RefTime;
// this won't solve the out of sync problem
// but at least the streams won't be dropped
for (i=0;i<p->Format.StreamCount;++i)
{
avistream* s = (avistream*) p->Format.Streams[i];
if (s->Stream.Reader == Reader)
{
RefTime = s->RefStart+Scale64(s->MediaTime,s->MediaRate.Num,s->MediaRate.Den);
if (RefTime < p->Format.ProcessTime - TICKSPERSEC)
SetMediaTime(s,Scale64(p->Format.ProcessTime-s->RefStart,s->MediaRate.Den,s->MediaRate.Num));
}
}
}
Reader->OutOfSync = 0;
}
Packet->Stream = &Stream->Stream;
if (Stream->ZeroChunkPos)
while (*Stream->ZeroChunkPos == Stream->MediaTime)
{
Stream->MediaTime += ChunkTime(Stream,0);
++Stream->ZeroChunkPos;
}
if (Stream->Stream.Format.Type == PACKET_SUBTITLE)
{
if (!SubtitleTiming(Packet,Reader,DataLength))
continue;
}
else
{
Packet->Data = Reader->ReadAsRef(Reader,DataLength);
Packet->RefTime = Stream->RefStart+Scale64(Stream->MediaTime,Stream->MediaRate.Num,Stream->MediaRate.Den);
}
DEBUG_MSG5(DEBUG_FORMAT,T("AVI stream:%d time:%d mediatime:%d pos:%08x length:%08x"),No,Packet->RefTime,Stream->MediaTime,Reader->FilePos,DataLength);
Stream->MediaTime += ChunkTime(Stream,DataLength);
return ERR_NONE;
}
}
if (ISIDX(Type))
{
No = IDXNO(Type);
if (No >= 0 && No < p->Format.StreamCount)
break; // opendml index
}
switch (Type)
{
case FOURCCLE('R','I','F','F'):
Reader->ReadLE32(Reader); // 'AVI ' or 'AVIX'
return ERR_NONE;
case FOURCCLE('a','v','i','h'):
if (p->Format.HeaderLoaded) break;
p->FramePeriod = Reader->ReadLE32(Reader);
p->ByteRate = Reader->ReadLE32(Reader);
DataLength -= 8;
break;
case FOURCCLE('i','n','d','x'):
if (p->Format.HeaderLoaded) break;
// opendml index
LongsPerEntry = Reader->ReadLE16(Reader);
Reader->Read8(Reader); //subtype
IndexType = Reader->Read8(Reader);
Count = Reader->ReadLE32(Reader);
Type = Reader->ReadLE32(Reader);
BaseOffset = (int)Reader->ReadLE64(Reader);
Reader->Skip(Reader,4);
DataLength -= 6*4;
if (ISDATA(Type))
{
No = STREAMNO(Type);
if (No >= 0 && No < p->Format.StreamCount && Count)
{
avisuperindex* s;
Stream = (avistream*) p->Format.Streams[No];
if (IndexType == AVI_INDEX_OF_CHUNKS && LongsPerEntry==2)
{
p->UnLimited = 1;
p->Format.FileSize = -1;
// create fake index of indexes
Stream->SuperIndexCount = 1;
Stream->SuperIndex = s = (avisuperindex*) malloc(sizeof(avisuperindex));
s->Offset = Reader->FilePos - 6*4 - 8;
s->Size = DataLength + 6*4 + 8;
s->StartTime = 0;
s->EndTime = 0;
s->LastData = 0;
for (No=0;No<Count;++No)
{
int Data = BaseOffset + Reader->ReadLE32(Reader) - 8;
int Size = Reader->ReadLE32(Reader) & ~AVI_INDEX_KEY;
if (s->LastData < Data)
s->LastData = Data;
s->EndTime += ChunkTime(Stream,Size);
}
}
if (IndexType == AVI_INDEX_OF_INDEXES && LongsPerEntry==4)
{
int Time;
p->UnLimited = 1;
p->Format.FileSize = -1;
// load index of indexes
Stream->SuperIndexCount = Count;
Stream->SuperIndex = s = (avisuperindex*) malloc(sizeof(avisuperindex)*Count);
Time = 0;
for (No=0;No<Count;++No,++s)
{
s->Offset = (int)Reader->ReadLE64(Reader);
s->Size = Reader->ReadLE32(Reader);
s->StartTime = Time;
Time += Reader->ReadLE32(Reader) * Stream->SampleSize;
s->EndTime = Time;
s->LastData = -1;
DataLength -= 16;
}
if (Count && !p->FoundStdIndex && !p->CheckForZero && Stream->Stream.Format.Type == PACKET_VIDEO)
{
LockEnter(p->Format.InputLock);
PreCheckIndexODML(p,Stream);
LockLeave(p->Format.InputLock);
}
}
}
}
break;
case FOURCCLE('s','t','r','h'):
if (p->Format.HeaderLoaded) break;
p->HeadType = Reader->ReadLE32(Reader);
DataLength -= 4;
if (p->HeadType == FOURCCLE('v','i','d','s'))
{
p->VideoHandler = Reader->ReadLE32(Reader);
Reader->ReadLE32(Reader); // flags
Reader->ReadLE16(Reader); // priority
Reader->ReadLE16(Reader); // language
Reader->ReadLE32(Reader);
p->VideoFPS.Den = Reader->ReadLE32(Reader);
p->VideoFPS.Num = Reader->ReadLE32(Reader);
if (!p->VideoFPS.Den || !p->VideoFPS.Num)
{
p->VideoFPS.Den = 1;
p->VideoFPS.Num = 25;
}
if (p->VideoFPS.Den==1 && p->VideoFPS.Num >= 12000) //not important
p->VideoFPS.Den = 1000;
p->VideoStart = Reader->ReadLE32(Reader);
p->VideoFrames = Reader->ReadLE32(Reader);
DataLength -= 32;
}
else
if (p->HeadType == FOURCCLE('a','u','d','s'))
{
Reader->ReadLE32(Reader); // format
Reader->ReadLE32(Reader); // flags
Reader->ReadLE16(Reader); // priority
Reader->ReadLE16(Reader); // language
Reader->ReadLE32(Reader);
p->AudioRate.Den = Reader->ReadLE32(Reader);
p->AudioRate.Num = Reader->ReadLE32(Reader);
p->AudioStart = Reader->ReadLE32(Reader);
p->AudioLength = Reader->ReadLE32(Reader);
Reader->ReadLE32(Reader); // initialframes
Reader->ReadLE32(Reader); // suggested buffersize
p->AudioSampleSize = Reader->ReadLE32(Reader);
DataLength -= 44;
}
break;
case FOURCCLE('s','t','r','n'):
if (p->Format.HeaderLoaded) break;
if (p->LastStream && DataLength)
ReadMeta(p,Reader,&DataLength);
break;
case FOURCCLE('s','t','r','f'):
if (p->Format.HeaderLoaded) break;
if (p->HeadType == FOURCCLE('v','i','d','s'))
{
p->LastStream = Stream = (avistream*) Format_AddStream(&p->Format,sizeof(avistream));
if (Stream)
{
Stream->Index = NULL;
Stream->SuperIndex = NULL;
Stream->ByteRate = 0;
Stream->Stream.Fragmented = 0;
Stream->SampleSize = 1;
Stream->MaxPacket = MAX_INT;
Format_BitmapInfo(Reader,&Stream->Stream,DataLength);
if (Stream->Stream.Format.Format.Video.Pixel.FourCC == FOURCC('D','X','S','B'))
{
PacketFormatClear(&Stream->Stream.Format);
Stream->Stream.Format.Type = PACKET_SUBTITLE;
Stream->Stream.Format.Format.Subtitle.FourCC = FOURCC('D','X','S','B');
}
else
{
if (Stream->Stream.Format.Format.Video.Pixel.FourCC == 4)
Stream->Stream.Format.Format.Video.Pixel.FourCC = UpperFourCC(p->VideoHandler);
Stream->MediaRate.Den = p->VideoFPS.Num;
Stream->MediaRate.Num = (int64_t)p->VideoFPS.Den * TICKSPERSEC;
Stream->RefStart = Scale64(p->VideoStart,Stream->MediaRate.Num,Stream->MediaRate.Den);
Stream->Length = p->VideoFrames;
Stream->Stream.Format.PacketRate = p->VideoFPS;
if (p->VideoFrames)
{
Duration = Scale64(p->VideoFrames,Stream->MediaRate.Num,Stream->MediaRate.Den);
if (p->Format.Duration < Duration)
p->Format.Duration = Duration;
}
UpdateAviFrameRate(p);
}
Format_PrepairStream(&p->Format,&Stream->Stream);
}
else
Reader->Skip(Reader,DataLength);
return ERR_NONE;
}
else
if (p->HeadType == FOURCCLE('a','u','d','s'))
{
p->LastStream = Stream = (avistream*) Format_AddStream(&p->Format,sizeof(avistream));
if (Stream)
{
Format_WaveFormat(Reader,&Stream->Stream,DataLength);
// some encoders doesn't set AudioSampleSize, but still using CBR mode
// detect it by insane packet rate
if (!p->AudioSampleSize && p->AudioRate.Den*1000<=p->AudioRate.Num)
p->AudioSampleSize = Stream->Stream.Format.Format.Audio.BlockAlign;
Stream->Index = NULL;
Stream->SuperIndex = NULL;
Stream->ByteRate = p->AudioSampleSize != 0;
Stream->Stream.Fragmented = Stream->ByteRate && p->AudioSampleSize <= 4;
if (!Stream->ByteRate)
{
// VBR (packetrate)
Stream->MediaRate.Den = p->AudioRate.Num;
Stream->MediaRate.Num = (int64_t)p->AudioRate.Den * TICKSPERSEC;
if (p->AudioRate.Num == Stream->Stream.Format.Format.Audio.SampleRate)
Stream->MaxPacket = p->AudioRate.Den;
else
Stream->MaxPacket = MAX_INT;
Stream->SampleSize = 1;
}
else
{
// CBR (byterate)
// some digital cameras don't fill AudioSampleSize correctly only BlockAlign (with ADPCM)
if (p->AudioSampleSize < Stream->Stream.Format.Format.Audio.BlockAlign)
p->AudioSampleSize = Stream->Stream.Format.Format.Audio.BlockAlign;
if (Stream->Stream.Format.Format.Audio.Format == 0x55 /*MP3*/ &&
p->AudioRate.Num == 4977)
{
Stream->MediaRate.Den = (p->AudioRate.Num*16-7)*p->AudioSampleSize;
Stream->MediaRate.Num = (int64_t)p->AudioRate.Den*TICKSPERSEC*16;
}
else
{
Stream->MediaRate.Den = p->AudioRate.Num*p->AudioSampleSize;
Stream->MediaRate.Num = (int64_t)p->AudioRate.Den*TICKSPERSEC;
}
Stream->SampleSize = p->AudioSampleSize;
}
Stream->RefStart = Scale64(p->AudioStart,(int64_t)p->AudioRate.Den*TICKSPERSEC,p->AudioRate.Num);
Stream->Length = p->AudioLength;
if (p->AudioLength)
{
Duration = Scale64(p->AudioLength,(int64_t)p->AudioRate.Den*TICKSPERSEC,p->AudioRate.Num);
if (Duration>0 && p->Format.Duration < Duration)
p->Format.Duration = Duration;
}
UpdateAviFrameRate(p);
Format_PrepairStream(&p->Format,&Stream->Stream);
}
else
Reader->Skip(Reader,DataLength);
return ERR_NONE;
}
else
p->LastStream = NULL;
break;
case FOURCCLE('L','I','S','T'):
SubType = Reader->ReadLE32(Reader);
DataLength -= 4;
switch (SubType)
{
case FOURCCLE('s','t','r','l'):
return ERR_NONE;
case FOURCCLE('h','d','r','l'):
return ERR_NONE;
case FOURCCLE('m','o','v','i'):
if (p->Format.HeaderLoaded) return ERR_NONE;
p->DataPos = Reader->FilePos;
p->DataEnd = DataLength + Reader->FilePos;
p->Format.HeaderLoaded = 1;
if (p->FoundTotal >= 256 && p->Format.StreamCount>1)
{
for (No=0;No<p->Format.StreamCount && No<8;++No)
if (p->FoundAtEnd[No] < 2)
p->CheckForZero = 1; // possibly non interleaved streams
}
if (p->CheckForZero)
{
LockEnter(p->Format.InputLock);
CheckIndex(p,Reader);
LockLeave(p->Format.InputLock);
}
for (No=0;No<p->Format.StreamCount;++No)
SetMediaTime((avistream*)p->Format.Streams[No],0);
return ERR_NONE;
case FOURCCLE('o','d','m','l'):
break;
default:
return ERR_NONE;
}
break;
case FOURCCLE('J','U','N','K'):
case FOURCCLE('i','d','x','1'):
break;
default:
if (Reader->OutOfSync || DataLength > 256*1024) // unknown
DataLength = -1;
}
}
while (DataLength<0);
Reader->Skip(Reader,DataLength);
return ERR_NONE;
}
static int ErrorIndex(avi* p,stream* Input,filepos_t OldFilePos)
{
if (OldFilePos>=0)
{
if (Input)
Input->Seek(Input,OldFilePos,SEEK_SET);
if (!p->ErrorIndex)
{
p->ErrorIndex = 1;
ShowError(p->Format.Format.Class,AVI_ID,AVI_NO_INDEX);
}
}
return ERR_NOT_SUPPORTED;
}
static INLINE const aviindex* IndexBuffer(avi* p,int n)
{
return ((const aviindex*)p->IndexBuffer.Ptr)+n;
}
static int LoadIndexBuffer(avi* p,stream* Input,int No,filepos_t OldFilePos)
{
if (p->IndexBufferNo != No)
{
int Bytes = (p->IndexCount - No*INDEXSTEP)*sizeof(aviindex);
if (Bytes > INDEXSTEP*sizeof(aviindex))
Bytes = INDEXSTEP*sizeof(aviindex);
if (Input->Seek(Input,p->IndexPos+No*INDEXSTEP*sizeof(aviindex),SEEK_SET)<0)
return ErrorIndex(p,Input,OldFilePos);
if (Input->ReadBlock(Input,&p->IndexBuffer,0,Bytes) != Bytes)
return ErrorIndex(p,Input,OldFilePos);
p->IndexBufferNo = No;
if (No == 0)
{
if (INT32LE(IndexBuffer(p,0)->Pos) == p->DataPos)
p->IndexAdjustPos = p->DataPos;
else
p->IndexAdjustPos = 4;
}
}
return ERR_NONE;
}
static int ReadIndex(stream* Input,avistream* Stream,avisuperindex* Index,int MediaTime,int DstPos,bool_t PrevKey)
{
bool_t NeedKey = Stream->Stream.Format.Type == PACKET_VIDEO;
avistdindex StdIndex;
int No;
int FoundPos = -1;
int32_t* i;
int LookBack = 0;
if (!NeedKey)
PrevKey = 0;
if (PrevKey)
LookBack = 300;
if (DstPos>=0 && Index->LastData>=0 && DstPos > Index->LastData)
return -1;
if (MediaTime>=0 && Index->EndTime + LookBack <= MediaTime)
return -1;
if (Input->Seek(Input,Index->Offset,SEEK_SET) < 0)
return -2;
if (Input->Read(Input,&StdIndex,sizeof(StdIndex)) != sizeof(StdIndex))
return -1;
if (StdIndex.IndexType != AVI_INDEX_OF_CHUNKS ||
StdIndex.LongsPerEntry != 2)
return -1;
i = (int32_t*) malloc(8*StdIndex.Count);
if (!i)
return -1;
if (Input->Read(Input,i,8*StdIndex.Count) != 8*StdIndex.Count)
{
free(i);
return -1;
}
if (Index->LastData<0)
{
Index->LastData = 0;
for (No=0;No<StdIndex.Count;++No)
{
int Data = StdIndex.OffsetLow + i[No*2] - 8;
int Size = i[No*2+1] & ~AVI_INDEX_KEY;
if (Size && Index->LastData < Data)
Index->LastData = Data;
}
}
Stream->NewMediaTime = Index->StartTime;
for (No=0;No<StdIndex.Count;++No)
{
int Data = StdIndex.OffsetLow + i[No*2] - 8;
int Size = i[No*2+1];
if (MediaTime>=0)
{
if (Stream->NewMediaTime >= MediaTime && (!NeedKey || !(Size & AVI_INDEX_KEY)))
{
if (PrevKey && Stream->NewMediaTime != MediaTime && Stream->PrevKey>=0)
{
Stream->NewMediaTime = Stream->PrevMediaTime;
FoundPos = Stream->PrevKey; // use previous key
}
else
FoundPos = Data; // found sample after mediatime
break;
}
if (PrevKey && !(Size & AVI_INDEX_KEY))
{
Stream->PrevMediaTime = Stream->NewMediaTime;
Stream->PrevKey = Data;
}
}
if (DstPos>=0 && Data >= DstPos)
{
FoundPos = Data; // found a sample after the destination position
break;
}
Stream->NewMediaTime = Stream->NewMediaTime + ChunkTime(Stream,Size & ~AVI_INDEX_KEY);
}
free(i);
return FoundPos;
}
static int RestorePrevKey(avi* p,avistream* Stream,int No,int OldFilePos,int* Pos)
{
int i,j;
avistream* s;
if (Stream->PrevKeyBuffer != No)
{
int Result = LoadIndexBuffer(p,Stream->Stream.Reader->Input,Stream->PrevKeyBuffer,OldFilePos);
if (Result != ERR_NONE)
return Result;
No = Stream->PrevKeyBuffer;
}
// restart this block and recalc NewMediaTime
for (j=0;j<p->Format.StreamCount;++j)
{
s = (avistream*) p->Format.Streams[j];
s->NewMediaTime = s->Index[No];
}
for (i=0;i<Stream->PrevKey;++i)
{
j = STREAMNO(IndexBuffer(p,i)->Id);
if (j>=0 && j<p->Format.StreamCount)
{
s = (avistream*) p->Format.Streams[j];
s->NewMediaTime += ChunkTime(s,IndexBuffer(p,i)->Length);
}
}
*Pos = INT32LE(IndexBuffer(p,i)->Pos) - p->IndexAdjustPos;
return ERR_NONE;
}
static int FindIndexPos(avi* p,stream* Input)
{
int OldFilePos = Input->Seek(Input,0,SEEK_CUR);
int Pos = p->DataEnd;
// try to find index block
if (Input->Seek(Input,Pos,SEEK_SET) < 0)
return ERR_NOT_SUPPORTED;
for (;;)
{
int32_t Head[2];
if (Pos & 1)
{
if (Input->Read(Input,Head,sizeof(Head)) != sizeof(Head))
return ErrorIndex(p,Input,OldFilePos);
Pos += sizeof(Head);
if (Head[0] != FOURCC('i','d','x','1'))
{
memmove((char*)Head,(char*)Head+1,sizeof(Head)-1);
if (Input->Read(Input,(char*)Head+sizeof(Head)-1,1) != 1)
return ErrorIndex(p,Input,OldFilePos);
++Pos;
}
}
else
{
if (Input->Read(Input,Head,sizeof(Head)) != sizeof(Head))
return ErrorIndex(p,Input,OldFilePos);
Pos += sizeof(Head);
}
if (Head[0] == FOURCC('i','d','x','1'))
{
int i,No;
p->IndexCount = Head[1] / sizeof(aviindex);
i = ((p->IndexCount + INDEXSTEP - 1) / INDEXSTEP) + 1;
for (No=0;No<p->Format.StreamCount;++No)
{
avistream* s = (avistream*) p->Format.Streams[No];
s->Index = (int*) malloc(sizeof(int)*i);
if (!s->Index)
return ERR_OUT_OF_MEMORY;
s->Index[0] = 0;
}
p->IndexPos = Pos;
p->IndexReaded = 0;
break;
}
if (Head[1]<0 || (Pos+Head[1]<0))
return ErrorIndex(p,Input,OldFilePos);
Pos += Head[1];
if (Input->Seek(Input,Pos,SEEK_SET) != Pos)
return ErrorIndex(p,Input,OldFilePos);
}
return ERR_NONE;
}
static NOINLINE int CheckIndexBuffer(avi* p,stream* Input,int No,filepos_t OldFilePos,int IndexCount)
{
// load page and calc sum mediatime
if (p->IndexReaded <= No)
{
int i,j;
int Result = LoadIndexBuffer(p,Input,No,OldFilePos);
if (Result != ERR_NONE)
return Result;
for (j=0;j<p->Format.StreamCount;++j)
{
avistream* s = (avistream*) p->Format.Streams[j];
s->Index[No+1] = s->Index[No];
}
for (i=0;i<IndexCount;++i)
{
j = STREAMNO(IndexBuffer(p,i)->Id);
if (j>=0 && j<p->Format.StreamCount)
{
avistream* s = (avistream*) p->Format.Streams[j];
s->Index[No+1] += ChunkTime(s,INT32LE(IndexBuffer(p,i)->Length));
}
}
p->IndexReaded = No+1;
}
return ERR_NONE;
}
static int Seek(avi* p, tick_t Time, filepos_t FilePos, bool_t PrevKey)
{
int Result = ERR_NONE;
int RNo;
if (p->DataPos < 0)
{
// no avi header yet, only seek to the beginning supported
if (Time>0 || FilePos>0)
return ERR_NOT_SUPPORTED;
p->Format.Reader[0].OutOfSync = 0;
return Format_Seek(&p->Format,0,SEEK_SET);
}
for (RNo=0;RNo<MAXREADER;++RNo)
{
filepos_t Pos = -1;
avistream* Stream;
avistream* s;
int i,j,No;
format_reader* Reader = &p->Format.Reader[RNo];
stream* Input = Reader->Input;
filepos_t OldReaderPos = Reader->FilePos;
filepos_t OldFilePos;
if (!Input)
break;
OldFilePos = Input->Seek(Input,0,SEEK_CUR);
if (Time <= 0 && FilePos <= 0)
{
// seek to the beginning (no index needed for that)
Pos = Reader->OffsetMin<MAX_INT?Reader->OffsetMin:p->DataPos;
for (j=0;j<p->Format.StreamCount;++j)
if (p->Format.Streams[j]->Reader == Reader)
((avistream*)p->Format.Streams[j])->NewMediaTime = 0;
}
else
{
Stream = (avistream*) Format_DefSyncStream(&p->Format,Reader);
if (Stream)
{
bool_t NeedKey = Stream->Stream.Format.Type == PACKET_VIDEO;
int MediaTime = Scale64(Time,Stream->MediaRate.Den,Stream->MediaRate.Num);
Stream->PrevKey = -1;
if (PrevKey)
++MediaTime; // seek to same position even if there is rounding error
if (Stream->SuperIndex)
{
// using OpenDML index
if (MediaTime >= Stream->SuperIndex[Stream->SuperIndexCount-1].EndTime)
MediaTime = Stream->SuperIndex[Stream->SuperIndexCount-1].EndTime-1;
// find position by MediaTime
for (No=0;No<Stream->SuperIndexCount;++No)
{
Pos = ReadIndex(Input,Stream,Stream->SuperIndex+No,MediaTime,-1,PrevKey);
if (Pos == -2)
return ERR_NOT_SUPPORTED;
if (Pos >= 0)
break;
}
if (Pos<0)
{
if (PrevKey && Stream->PrevKey>=0)
{
Stream->NewMediaTime = Stream->PrevMediaTime;
Pos = Stream->PrevKey;
}
else
{
Stream->NewMediaTime = Stream->SuperIndex[Stream->SuperIndexCount-1].EndTime;
Pos = Stream->SuperIndex[Stream->SuperIndexCount-1].LastData;
if (Pos<0)
return ErrorIndex(p,Input,OldFilePos);
}
}
// calculate NewMediaTime for all other streams
for (j=0;j<p->Format.StreamCount;++j)
{
s = (avistream*) p->Format.Streams[j];
if (s->Stream.Reader==Reader && s != Stream)
{
// find position by Pos
for (No=0;No<s->SuperIndexCount;++No)
if (ReadIndex(Input,s,s->SuperIndex+No,-1,Pos,0) >= 0)
break;
if (No==s->SuperIndexCount)
s->NewMediaTime = s->SuperIndex[s->SuperIndexCount-1].EndTime;
}
}
}
else
{
int Next;
int IndexCount,IndexLeft;
tick_t LookBack = 0;
// do we have the index block?
if (p->IndexPos < 0)
{
Result = FindIndexPos(p,Input);
if (Result!=ERR_NONE)
return Result;
}
IndexLeft = p->IndexCount;
if (!NeedKey)
PrevKey = 0;
if (PrevKey)
LookBack = 300;
// find the index page
for (No=0;IndexLeft>0;++No,IndexLeft-=INDEXSTEP)
{
IndexCount = IndexLeft;
if (IndexCount > INDEXSTEP)
IndexCount = INDEXSTEP;
Result = CheckIndexBuffer(p,Input,No,OldFilePos,IndexCount);
if (Result != ERR_NONE)
return Result;
//DEBUG_MSG4(DEBUG_FORMAT,"AVI IndexBlock no:%d start:%d end:%d (target:%d)",No,Stream->Index[No],Stream->Index[No+1],MediaTime);
if (Stream->Index[No]!=Stream->Index[No+1] &&
(Stream->Index[No+1]+LookBack > MediaTime || IndexLeft<2*INDEXSTEP))
{
Result = LoadIndexBuffer(p,Input,No,OldFilePos);
if (Result != ERR_NONE)
return Result;
for (j=0;j<p->Format.StreamCount;++j)
{
s = (avistream*) p->Format.Streams[j];
if (s->Stream.Reader == Reader)
s->NewMediaTime = s->Index[No];
}
// find sub page position
for (i=0;i<IndexCount;++i)
{
j = STREAMNO(IndexBuffer(p,i)->Id);
if (j>=0 && j<p->Format.StreamCount && p->Format.Streams[j]->Reader==Reader)
{
s = (avistream*) p->Format.Streams[j];
Next = s->NewMediaTime + ChunkTime(s,IndexBuffer(p,i)->Length);
//DEBUG_MSG3(DEBUG_FORMAT,"AVI Index stream:%d pos:%d flag:%d",j,INT32LE(IndexBuffer(p,i)->Pos),IndexBuffer(p,i)->Flags[0]);
if (s==Stream)
{
Pos = INT32LE(IndexBuffer(p,i)->Pos) - p->IndexAdjustPos;
if (s->NewMediaTime >= MediaTime)
{
// only accept keyframes with valid position
if (Pos >= 0 && Pos < p->DataEnd - p->DataPos &&
(!NeedKey || (IndexBuffer(p,i)->Flags[0] & 0x10)))
{
if (PrevKey && s->NewMediaTime != MediaTime && Stream->PrevKey>=0)
{
Result = RestorePrevKey(p,Stream,No,OldFilePos,&Pos);
if (Result != ERR_NONE)
return Result;
}
break;
}
}
if (Pos >= 0 && Pos < p->DataEnd - p->DataPos &&
(IndexBuffer(p,i)->Flags[0] & 0x10))
{
Stream->PrevKey = i;
Stream->PrevKeyBuffer = No;
}
}
s->NewMediaTime = Next;
}
}
// found?
if (i<IndexCount)
break;
}
}
if (IndexLeft <= 0)
{
if (Stream->PrevKey>=0)
{
Result = RestorePrevKey(p,Stream,No,OldFilePos,&Pos);
if (Result != ERR_NONE)
return Result;
}
else
{
// go to end
for (j=0;j<p->Format.StreamCount;++j)
{
s = (avistream*) p->Format.Streams[j];
s->NewMediaTime = s->Index[p->IndexReaded];
}
Pos = p->DataEnd - p->DataPos;
}
}
Pos += p->DataPos;
}
if (NeedKey && Stream->NewMediaTime >= 0) // use updated time with other Readers
Time = Scale64(Stream->NewMediaTime,Stream->MediaRate.Num,Stream->MediaRate.Den);
}
}
if (Pos>=0)
{
Reader->FilePos = -1; // force seek, because Input changed
if (Reader->Seek(Reader,Pos,SEEK_SET) != ERR_NONE)
{
Reader->FilePos = OldReaderPos;
// restore old input position
Input->Seek(Input,OldFilePos,SEEK_SET);
return ERR_NOT_SUPPORTED;
}
// set new positions
for (j=0;j<p->Format.StreamCount;++j)
if (p->Format.Streams[j]->Reader == Reader)
{
s = (avistream*) p->Format.Streams[j];
SetMediaTime(s,s->NewMediaTime);
}
Reader->OutOfSync = 0;
}
}
Format_AfterSeek(&p->Format);
return ERR_NONE;
}
static NOINLINE void AddZeroChunk(avistream* Stream)
{
ArrayAppend(&Stream->ZeroChunk,&Stream->NewMediaTime,sizeof(Stream->NewMediaTime),512);
}
static void CheckIndex(avi* p, format_reader* Reader)
{
bool_t FlushBuffers = 0;
avistream* Stream;
int i,j,k;
bool_t OpenDML = 0;
array Array = {NULL};
stream* Input = Reader->Input;
filepos_t OldFilePos = Input->Seek(Input,0,SEEK_CUR);
filepos_t OldReaderPos = Reader->FilePos;
if (OldFilePos<0)
return;
for (i=0;i<p->Format.StreamCount;++i)
{
Stream = (avistream*)p->Format.Streams[i];
Stream->NewMediaTime = 0;
Stream->OffsetMin = MAX_INT;
Stream->OffsetMax = 0;
Stream->LastPos = 0;
if (Stream->SuperIndex) // we need non video streams as well for offsetmin/max
{
OpenDML = 1;
for (j=0;j<Stream->SuperIndexCount;++j)
{
avistdindex StdIndex;
if (Input->Seek(Input,Stream->SuperIndex[j].Offset,SEEK_SET) < 0)
break;
if (Input->Read(Input,&StdIndex,sizeof(StdIndex)) != sizeof(StdIndex))
break;
if (StdIndex.IndexType != AVI_INDEX_OF_CHUNKS ||
StdIndex.LongsPerEntry != 2)
break;
ArrayDrop(&Array);
if (!ArrayAppend(&Array,NULL,8*StdIndex.Count,1024))
break;
if (Input->Read(Input,ARRAYBEGIN(Array,int32_t),8*StdIndex.Count) != 8*StdIndex.Count)
break;
for (k=0;k<StdIndex.Count;++k)
{
int Pos = StdIndex.OffsetLow + ARRAYBEGIN(Array,int32_t)[k*2] - 8;
int Size = ARRAYBEGIN(Array,int32_t)[k*2+1] & ~AVI_INDEX_KEY;
if (!Size)
AddZeroChunk(Stream);
else
{
if (Stream->OffsetMin>Pos)
Stream->OffsetMin=Pos;
if (Stream->OffsetMax<Pos)
Stream->OffsetMax=Pos;
}
Stream->NewMediaTime = Stream->NewMediaTime + ChunkTime(Stream,Size);
}
}
}
}
if (!OpenDML && (p->IndexPos>=0 || FindIndexPos(p,Input)==ERR_NONE))
{
int IndexLeft = p->IndexCount;
int IndexCount;
int LastPos = 0;
for (i=0;IndexLeft>0;++i,IndexLeft-=INDEXSTEP)
{
IndexCount = IndexLeft;
if (IndexCount > INDEXSTEP)
IndexCount = INDEXSTEP;
if (CheckIndexBuffer(p,Input,i,-1,IndexCount)==ERR_NONE &&
LoadIndexBuffer(p,Input,i,-1)==ERR_NONE)
{
for (k=0;k<IndexCount;++k)
{
j = STREAMNO(IndexBuffer(p,k)->Id);
if (j>=0 && j<p->Format.StreamCount)
{
int Pos = IndexBuffer(p,k)->Pos - p->IndexAdjustPos + p->DataPos;
int Size = IndexBuffer(p,k)->Length;
Stream = (avistream*) p->Format.Streams[j];
if ((Size==0 && Pos==LastPos) || (Pos==Stream->LastPos))
AddZeroChunk(Stream);
if (Size)
{
if (Stream->OffsetMin>Pos)
Stream->OffsetMin=Pos;
if (Stream->OffsetMax<Pos)
Stream->OffsetMax=Pos;
}
LastPos = Pos;
Stream->LastPos=Pos;
Stream->NewMediaTime = Stream->NewMediaTime + ChunkTime(Stream,Size);
}
}
}
}
}
for (i=0;i<p->Format.StreamCount;++i)
{
Stream = (avistream*)p->Format.Streams[i];
if (!ARRAYEMPTY(Stream->ZeroChunk))
{
Stream->NewMediaTime = MAX_INT;
AddZeroChunk(Stream);
}
if (Stream->OffsetMax)
{
format_reader* Reader = Format_FindReader(&p->Format,Stream->OffsetMin,Stream->OffsetMax);
if (Reader)
Stream->Stream.Reader = Reader;
}
}
ArrayClear(&Array);
for (i=0;i<MAXREADER;++i)
{
format_reader* v = &p->Format.Reader[i];
if (v->Input && v->OffsetMax>0)
{
v->Seek(v,v->OffsetMin,SEEK_SET);
FlushBuffers = 1;
if (Reader == v)
OldReaderPos = v->OffsetMin;
}
}
if (!FlushBuffers)
Input->Seek(Input,OldFilePos,SEEK_SET);
else
{
// probably first reader already allocated all the buffers
Reader->FilePos = -1;
Reader->Seek(Reader,OldReaderPos,SEEK_SET);
}
}
static int Set(avi* p,int No,const void* Data,int Size)
{
if (No == NODE_SETTINGSCHANGED)
UpdateAviFrameRate(p);
return FormatBaseSet(&p->Format,No,Data,Size);
}
static void FreeStream(avi* p, avistream* Stream)
{
free(Stream->Index);
free(Stream->SuperIndex);
ArrayClear(&Stream->ZeroChunk);
}
static int Create(avi* p)
{
p->Format.Format.Set = (nodeset)Set;
p->Format.Init = (fmtfunc)Init;
p->Format.Done = (fmtvoid)Done;
p->Format.Seek = (fmtseek)Seek;
p->Format.ReadPacket = (fmtreadpacket)ReadPacket;
p->Format.FreeStream = (fmtstream)FreeStream;
p->Format.MinHeaderLoad = MAX_INT; //manualy setting HeaderLoaded
return ERR_NONE;
}
static const nodedef AVI =
{
sizeof(avi),
AVI_ID,
FORMATBASE_CLASS,
PRI_DEFAULT,
(nodecreate)Create,
};
void AVI_Init()
{
NodeRegisterClass(&AVI);
}
void AVI_Done()
{
NodeUnRegisterClass(AVI_ID);
}