format_base.c
资源名称:tcpmp.rar [点击查看]
上传用户:wstnjxml
上传日期:2014-04-03
资源大小:7248k
文件大小:66k
源码类别:
Windows CE
开发平台:
C/C++
- /*****************************************************************************
- *
- * 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: format_base.c 611 2006-01-25 22:01:51Z picard $
- *
- * The Core Pocket Media Player
- * Copyright (c) 2004-2005 Gabor Kovacs
- *
- ****************************************************************************/
- #include "common.h"
- #define DROP_SHOW 8
- #define ENDCHUNKS (128*1024/BLOCKSIZE)
- #define SKIPDISTANCE 64*1024
- #define SYNCREAD 6
- #define SEEKTRIES 4
- #define SEEKMAXREAD 256*1024
- #define PROCESSMINAUDIO BLOCKSIZE
- #define PROCESSMINVIDEO 128*1024
- #define READER_BONUS 256*1024
- int FormatBaseEnum(format_base* p,int* No,datadef* Param)
- {
- if (FormatEnum(p,No,Param) == ERR_NONE)
- return ERR_NONE;
- if (*No < p->TotalCount)
- {
- memset(Param,0,sizeof(datadef));
- Param->Class = FORMAT_CLASS;
- Param->Size = sizeof(pin);
- Param->No = FORMAT_STREAM + *No;
- Param->Type = TYPE_PACKET;
- Param->Flags = DF_OUTPUT;
- *No = 0;
- return ERR_NONE;
- }
- *No -= p->TotalCount;
- if (*No < p->TotalCount)
- {
- memset(Param,0,sizeof(datadef));
- Param->Class = FORMAT_CLASS;
- Param->Size = sizeof(pin);
- Param->No = FORMAT_COMMENT + *No;
- Param->Type = TYPE_COMMENT;
- Param->Flags = DF_OUTPUT;
- *No = 0;
- return ERR_NONE;
- }
- *No -= p->TotalCount;
- return ERR_INVALID_PARAM;
- }
- static INLINE void Format_BufferInsert(format_reader* p,format_buffer* Buffer)
- {
- Buffer->Next = NULL;
- if (p->BufferLast)
- p->BufferLast->Next = Buffer;
- else
- p->BufferFirst = Buffer;
- p->BufferLast = Buffer;
- p->BufferAvailable += Buffer->Length;
- }
- format_buffer* Format_BufferAlloc(format_base*,int Mode); //0:optional 1:drop 2:new alloc
- format_packet* Format_PacketAlloc(format_base*);
- void Format_PacketRelease(format_base*, format_packet*);
- bool_t Format_ReadBuffer(format_reader*, bool_t ToRead);
- int Format_Seek(format_base* p,filepos_t Pos,int Origin)
- {
- int Result = p->Reader->Seek(p->Reader,Pos,Origin);
- if (Result == ERR_NONE)
- Format_AfterSeek(p);
- return Result;
- }
- int Format_SeekForce(format_base* p,filepos_t Pos,int Origin)
- {
- int Result;
- filepos_t Save = p->Reader[0].FilePos;
- p->Reader[0].FilePos = -1;
- Result = Format_Seek(p,Pos,Origin);
- if (Result != ERR_NONE)
- p->Reader[0].FilePos = Save;
- return Result;
- }
- int Format_SeekByPacket(format_base* p,tick_t Time,filepos_t FilePos,bool_t PrevKey)
- {
- int Try;
- int LastTime;
- int LastPos;
- format_stream* Stream = NULL;
- if (FilePos < 0)
- {
- Time -= SHOWAHEAD; // we should go a little earlier
- if (Time > 0)
- {
- if (p->FileSize<0 || p->Duration<0)
- return ERR_NOT_SUPPORTED;
- FilePos = Scale(p->FileSize,Time,p->Duration);
- }
- else
- FilePos = 0;
- }
- if (Time>0)
- Stream = Format_DefSyncStream(p,p->Reader);
- p->GlobalOffset = 0;
- p->InSeek = 1;
- LastPos = -1;
- LastTime = -1;
- for (Try=0;Try<SEEKTRIES;++Try) // maximum iterations
- {
- if (FilePos < p->SeekByPacket_DataStart)
- FilePos = p->SeekByPacket_DataStart;
- if (p->SeekByPacket_BlockAlign > 1)
- FilePos -= (FilePos - p->SeekByPacket_DataStart) % p->SeekByPacket_BlockAlign;
- if (Format_Seek(p,FilePos,SEEK_SET) != ERR_NONE)
- {
- p->InSeek = 0;
- return ERR_NOT_SUPPORTED;
- }
- if (Stream)
- {
- // check if really seeked to the right time (and adjust if needed)
- format_reader* Reader = p->Reader;
- int SeekRead = FilePos + SEEKMAXREAD;
- while (Reader->FilePos<SeekRead)
- {
- Format_ReadBuffer(Reader,0); // simulate input thread
- if (p->FillQueue(p,Reader) == ERR_END_OF_FILE)
- break;
- if (Stream->LastTime >= 0)
- {
- DEBUG_MSG3(DEBUG_FORMAT,T("SeekByPacket to:%d current:%d filepos:%d"),Time,Stream->LastTime,FilePos);
- if (!p->TimeStamps)
- {
- // adjust GlobalOffset
- p->GlobalOffset = Time - Stream->LastTime;
- if (p->GlobalOffset>0)
- {
- // need to adjust all stream packets
- int No;
- for (No=0;No<p->StreamCount;++No)
- {
- format_packet* i;
- format_stream* s = p->Streams[No];
- if (s->LastTime >= 0)
- s->LastTime += p->GlobalOffset;
- for (i=s->PacketFirst;i;i=i->Next)
- if (i->RefTime >= 0)
- i->RefTime += p->GlobalOffset;
- }
- }
- else
- p->GlobalOffset=0; // can't be negative
- SeekRead = -1; // accept
- break;
- }
- if (Time>=Stream->LastTime && (Time-Stream->LastTime) <= (TICKSPERSEC/2))
- FilePos = -1; // accept
- else
- {
- int Move;
- if (LastPos >= 0)
- Move = Scale(FilePos-LastPos,Time-Stream->LastTime,Stream->LastTime-LastTime);
- else
- Move = Scale(p->FileSize,Time-Stream->LastTime,p->Duration);
- if (Move < 0)
- Move = (Move*9)/8;
- else
- Move = (Move*15)/16;
- if (Move > 64*1024 || Move < -64*1024)
- {
- LastTime = Stream->LastTime;
- LastPos = FilePos;
- }
- else
- LastPos = -1;
- FilePos += Move;
- }
- if (FilePos < 0 || FilePos >= p->FileSize)
- SeekRead = -1; // accept
- break;
- }
- if (!Reader->BufferAvailable && !Reader->ReadBuffer && Reader->NoMoreInput)
- break; // end of file
- }
- if (Reader->FilePos<SeekRead)
- continue; // try again
- }
- break;
- }
- p->InSeek = 0;
- return ERR_NONE;
- }
- format_buffer* Format_BufferRemoveLast(format_reader* Reader)
- {
- format_buffer *Buffer,*Tail;
- LockEnter(Reader->Format->BufferLock);
- Buffer = Reader->BufferLast;
- if (Buffer)
- {
- Tail = Reader->BufferFirst;
- if (Tail == Buffer)
- Reader->BufferFirst = Tail = NULL;
- else
- {
- while (Tail && Tail->Next != Buffer)
- Tail = Tail->Next;
- if (Tail)
- Tail->Next = NULL;
- }
- Reader->BufferLast = Tail;
- Reader->BufferAvailable -= Buffer->Length;
- assert(Reader->BufferAvailable >= 0);
- }
- LockLeave(Reader->Format->BufferLock);
- return Buffer;
- }
- int Format_Hibernate(format_base* p, int Mode)
- {
- bool_t Changed = 0;
- if (p->InputLock)
- {
- int No;
- format_buffer *Buffer;
- LockEnter(p->InputLock);
- LockEnter(p->BufferLock);
- #ifndef TARGET_PALMOS
- if (Mode == HIBERNATE_HARD)
- {
- format_reader *Reader = p->Reader;
- filepos_t Seek = 0;
- if (Reader->InputBuffer)
- Seek += Reader->InputBuffer->Length;
- for (Buffer = Reader->BufferFirst;Buffer;Buffer = Buffer->Next)
- Seek += Buffer->Length;
- if (Seek && Reader->Input && Reader->Input->Seek(Reader->Input,-Seek,SEEK_CUR)>=0)
- {
- Reader->NoMoreInput = 0;
- if (Reader->InputBuffer)
- {
- Format_BufferRelease(p,Reader->InputBuffer);
- Reader->InputBuffer = NULL;
- }
- while ((Buffer = Format_BufferRemoveLast(Reader)) != NULL)
- Format_BufferRelease(p,Buffer);
- }
- }
- #endif
- while (p->FreeBuffers)
- {
- Buffer = p->FreeBuffers;
- p->FreeBuffers = Buffer->Next;
- FreeBlock(&Buffer->Block);
- free(Buffer);
- Changed = 1;
- }
- if (!Changed)
- for (No=0;No<MAXREADER;++No)
- for (Buffer = p->Reader[No].BufferFirst;Buffer;Buffer = Buffer->Next)
- if (SetHeapBlock(BLOCKSIZE,&Buffer->Block,HEAP_STORAGE))
- Changed = 1;
- LockLeave(p->BufferLock);
- LockLeave(p->InputLock);
- }
- return Changed ? ERR_NONE : ERR_OUT_OF_MEMORY;
- }
- static bool_t DropBuffer(format_base* p)
- {
- bool_t Dropped = 0;
- int No=0;
- for (No=0;No<p->StreamCount;++No)
- {
- format_stream* Stream = p->Streams[No];
- if (Stream->PacketFirst)
- {
- format_packet* Packet = Stream->PacketFirst;
- Stream->PacketFirst = Packet->Next;
- if (Stream->PacketLast == Packet)
- Stream->PacketLast = NULL;
- Format_PacketRelease(p,Packet);
- Stream->Pending = 0;
- Stream->State.DropLevel = 2;
- Stream->DropCount = DROP_SHOW;
- Dropped = 1;
- }
- }
- return Dropped;
- }
- format_buffer* Format_BufferAlloc(format_base* p,int Mode)
- {
- format_buffer* Buffer;
- retry:
- Buffer = p->FreeBuffers;
- if (!Buffer)
- {
- if (p->BufferUsed < p->BufferSize || Mode==2)
- {
- block Block;
- if (AllocBlock(BLOCKSIZE,&Block,Mode!=2,HEAP_ANY))
- {
- Buffer = (format_buffer*) malloc(sizeof(format_buffer));
- if (Buffer)
- Buffer->Block = Block;
- else
- FreeBlock(&Block);
- }
- }
- else
- if (Mode==0)
- return NULL;
- if (!Buffer)
- {
- if (Mode==1 && DropBuffer(p))
- goto retry;
- return NULL;
- }
- }
- else
- {
- assert(Buffer->RefCount==0);
- p->FreeBuffers = Buffer->Next;
- }
- Buffer->Length = 0;
- Buffer->Id = p->BufferId++;
- Buffer->RefCount = 1;
- Buffer->Next = NULL;
- ++p->BufferUsed;
- return Buffer;
- }
- static int SumByteRate(format_base* p)
- {
- // try to sum stream byterates
- int No;
- int ByteRate = 0;
- for (No=0;No<p->StreamCount;++No)
- {
- packetformat Format;
- p->Format.Get(p,(FORMAT_STREAM|PIN_FORMAT)+No,&Format,sizeof(Format));
- if (Format.ByteRate<=0)
- {
- if (p->Streams[No]->Pin.Node && p->ProcessTime == TIME_SYNC)
- {
- p->ProcessTime = 0;
- p->Process(p,p->Streams[No]); // try processing
- p->ProcessTime = TIME_SYNC;
- }
- p->Format.Get(p,(FORMAT_STREAM|PIN_FORMAT)+No,&Format,sizeof(Format));
- if (Format.ByteRate<=0)
- return p->SumByteRate; // failed
- }
- ByteRate += Format.ByteRate;
- }
- return ByteRate;
- }
- static bool_t IsVideo(format_base* p)
- {
- int No;
- for (No=0;No<p->StreamCount;++No)
- if (p->Streams[No]->Format.Type == PACKET_VIDEO)
- return 1;
- return 0;
- }
- static void FindCoverArt(format_base* p);
- static void CalcDuration(format_base* p)
- {
- if (p->Duration<0 && p->FileSize>=0)
- {
- if (p->TimeStamps)
- {
- // try to read the last packets and figure out Duration
- filepos_t SavePos;
- format_reader* Reader = p->Reader;
- LockEnter(p->InputLock);
- LockEnter(p->BufferLock);
- SavePos = Reader->Input->Seek(Reader->Input,0,SEEK_CUR);
- if (SavePos >= 0)
- {
- format_buffer* Buffer[ENDCHUNKS];
- int No,i;
- int Total = 0;
- tick_t Max=-1;
- format_reader Backup = *Reader;
- memset(Buffer,0,sizeof(Buffer));
- Reader->NoMoreInput = 1; //disable reading during ReadPacket
- for (No=0;No<ENDCHUNKS;++No)
- {
- filepos_t Pos = p->FileSize - (No+1)*BLOCKSIZE;
- if (Pos<0)
- {
- if (No)
- break;
- Pos = 0;
- }
- Buffer[No] = Format_BufferAlloc(p,2);
- if (!Buffer[No])
- break;
- if (Reader->Input->Seek(Reader->Input,Pos,SEEK_SET)<0)
- break;
- Buffer[No]->Length = Reader->Input->ReadBlock(Reader->Input,&Buffer[No]->Block,0,BLOCKSIZE);
- if (Buffer[No]->Length < 0)
- break;
- Total += Buffer[No]->Length;
- if (No)
- Buffer[No]->Next = Buffer[No-1];
- // setup temporary buffer
- Reader->InputBuffer = NULL;
- Reader->BufferAvailable = Total;
- Reader->BufferLast = Buffer[0];
- Reader->BufferFirst = Buffer[No];
- Reader->ReadPos = 0;
- Reader->ReadBuffer = NULL;
- Reader->FilePos = Pos;
- for (i=0;i<=No;++i)
- Buffer[i]->RefCount++;
- if (p->SeekByPacket_BlockAlign > 1)
- {
- int Rem = (Pos - p->SeekByPacket_DataStart) % p->SeekByPacket_BlockAlign;
- if (Rem)
- Reader->Skip(Reader,p->SeekByPacket_BlockAlign - Rem);
- }
- if (p->BackupPacketState)
- p->BackupPacketState(p,1);
- while (Reader->BufferLast || Reader->ReadBuffer)
- {
- int Result;
- format_packet* Packet = Format_PacketAlloc(p);
- if (!Packet)
- break;
- Result = p->ReadPacket(p,Reader,Packet);
- if (Result != ERR_NONE && Result != ERR_DATA_NOT_FOUND)
- {
- Reader->BufferLast = NULL;
- Reader->ReadBuffer = NULL;
- }
- else
- if (Result == ERR_NONE && Packet->Stream && Packet->RefTime>Max)
- Max = Packet->RefTime;
- Format_PacketRelease(p,Packet);
- }
- if (p->BackupPacketState)
- p->BackupPacketState(p,0);
- for (i=0;i<=No;++i)
- Buffer[i]->RefCount=1; // ugly
- if (Max >= 0)
- {
- p->Duration = Max;
- break;
- }
- }
- for (No=0;No<ENDCHUNKS;++No)
- if (Buffer[No])
- Format_BufferRelease(p,Buffer[No]);
- Reader->Input->Seek(Reader->Input,SavePos,SEEK_SET);
- *Reader = Backup;
- }
- LockLeave(p->BufferLock);
- LockLeave(p->InputLock);
- }
- if (p->StreamCount)
- {
- int ByteRate = SumByteRate(p);
- if (ByteRate>0)
- {
- tick_t Duration = Scale(p->FileSize,TICKSPERSEC,ByteRate);
- if (p->Duration<0 || p->Duration+3*TICKSPERSEC<Duration)
- {
- p->Duration = Duration;
- p->TimeStamps = 0;
- }
- }
- }
- }
- #if !defined(MULTITHREAD)
- if (IsVideo(p))
- p->AutoReadSize = 1; // force auto readsize
- #endif
- if (p->StreamCount && p->ProcessMinBuffer)
- {
- int ByteRate = SumByteRate(p);
- if (ByteRate<=0 && p->Duration>0)
- {
- int FileSize = p->FileSize;
- if (FileSize < 0 && p->Reader->Input)
- {
- LockEnter(p->InputLock);
- p->Reader->Input->Get(p->Reader->Input,STREAM_LENGTH,&FileSize,sizeof(FileSize));
- LockLeave(p->InputLock);
- }
- ByteRate = Scale(FileSize,TICKSPERSEC,p->Duration);
- }
- if (ByteRate>0)
- {
- while (ByteRate>p->ProcessMinBuffer*2 && p->ProcessMinBuffer < p->BufferSize*BLOCKSIZE/4)
- p->ProcessMinBuffer += BLOCKSIZE;
- if (p->AutoReadSize)
- {
- int No;
- int Rate = ByteRate / 16;
- // find video framerate
- for (No=0;No<p->StreamCount;++No)
- {
- packetformat Format;
- p->Format.Get(p,(FORMAT_STREAM|PIN_FORMAT)+No,&Format,sizeof(Format));
- if (Format.Type == PACKET_VIDEO && Format.PacketRate.Num>0)
- {
- Rate = Scale(ByteRate,Format.PacketRate.Den,Format.PacketRate.Num);
- break;
- }
- }
- p->ReadSize = 16384;
- while (p->ReadSize>1024 && Rate<5000)
- {
- Rate <<= 1;
- p->ReadSize >>= 1;
- }
- }
- }
- }
- #if defined(TARGET_SYMBIAN)
- if (p->ReadSize > 4096) // large reads interfere with audio (until multithreaded is not added...)
- p->ReadSize = 4096;
- #endif
- FindCoverArt(p);
- }
- void Format_BufferRelease(format_base* p, format_buffer* Buffer)
- {
- //DEBUG_MSG4(DEBUG_FORMAT,"Buffer:%d release %d->%d (%d)",Buffer->Id,Buffer->RefCount,Buffer->RefCount-1,p->BufferUsed);
- LockEnter(p->BufferLock);
- assert(Buffer->RefCount>0 && Buffer->RefCount<1000);
- if (--Buffer->RefCount <= 0)
- {
- assert(Buffer->RefCount==0);
- --p->BufferUsed;
- if (p->BufferUsed <= p->BufferSize)
- {
- Buffer->Next = p->FreeBuffers;
- p->FreeBuffers = Buffer;
- }
- else
- {
- FreeBlock(&Buffer->Block);
- free(Buffer);
- }
- }
- LockLeave(p->BufferLock);
- }
- format_buffer* Format_BufferRemove(format_reader* Reader)
- {
- format_buffer* Buffer;
- LockEnter(Reader->Format->BufferLock);
- Buffer = Reader->BufferFirst;
- if (Buffer)
- {
- Reader->BufferFirst = Buffer->Next;
- if (Reader->BufferLast == Buffer)
- Reader->BufferLast = NULL;
- Reader->BufferAvailable -= Buffer->Length;
- assert(Reader->BufferAvailable >= 0);
- }
- LockLeave(Reader->Format->BufferLock);
- return Buffer;
- }
- void Format_SingleRefRelease(format_base* p, format_ref* Ref)
- {
- if (Ref->Buffer)
- Format_BufferRelease(p,Ref->Buffer);
- Ref->Next = p->FreeRefs;
- p->FreeRefs = Ref;
- }
- void Format_ReleaseRef(format_base* p, format_ref* Ref)
- {
- while (Ref)
- {
- format_ref* Next = Ref->Next;
- Format_SingleRefRelease(p,Ref);
- Ref = Next;
- }
- }
- void Format_PacketRelease(format_base* p, format_packet* Packet)
- {
- Format_ReleaseRef(p,Packet->Data);
- Packet->Data = NULL;
- Packet->Next = p->FreePackets;
- p->FreePackets = Packet;
- }
- static NOINLINE void ReleaseStream(format_base* p,format_stream* Stream)
- {
- format_packet* Packet;
- Stream->State.DropLevel = 0;
- Stream->DropCount = 0;
- Stream->Pending = 0;
- Stream->LastTime = TIME_SYNC;
- while (Stream->PacketFirst)
- {
- Packet = Stream->PacketFirst;
- Stream->PacketFirst = Packet->Next;
- if (Stream->PacketLast == Packet)
- Stream->PacketLast = NULL;
- Format_PacketRelease(p,Packet);
- }
- if (p->ReleaseStream && Stream != p->SubTitle && Stream != p->CoverArt)
- p->ReleaseStream(p,Stream);
- }
- static int UpdateStreamPin(format_base* p,format_stream* Stream)
- {
- if (!Stream->Pin.Node)
- ReleaseStream(p,Stream);
- return ERR_NONE;
- }
- void Format_AfterSeek(format_base* p)
- {
- int No;
- // release stream packets
- for (No=0;No<p->StreamCount;++No)
- ReleaseStream(p,p->Streams[No]);
- if (p->SubTitle)
- ReleaseStream(p,p->SubTitle);
- p->LastRead = NULL;
- if (p->AfterSeek)
- p->AfterSeek(p);
- }
- void Format_ReleaseBuffers(format_reader* Reader)
- {
- format_buffer* Buffer;
- // release read buffers
- Reader->NoMoreInput = 0;
- Reader->ReadPos = 0;
- if (Reader->ReadBuffer)
- {
- Format_BufferRelease(Reader->Format,Reader->ReadBuffer);
- Reader->ReadBuffer = NULL;
- }
- if (Reader->InputBuffer)
- {
- Format_BufferRelease(Reader->Format,Reader->InputBuffer);
- Reader->InputBuffer = NULL;
- }
- while ((Buffer = Format_BufferRemove(Reader))!=NULL)
- Format_BufferRelease(Reader->Format,Buffer);
- }
- format_reader* Format_FindReader(format_base* p,filepos_t Min,filepos_t Max)
- {
- int No;
- filepos_t LimitMax;
- if (Max < Min+16)
- Max = Min+16;
- LimitMax = p->BufferSize*BLOCKSIZE/2;
- if (p->Duration>0)
- {
- // example a 55Mb/sec mjpeg with pcm audio can have large differences at end of file
- LimitMax = Scale(Max-Min,3*TICKSPERSEC,p->Duration);
- if (LimitMax < 512*1024)
- LimitMax = 512*1024;
- }
- for (No=0;No<MAXREADER;++No)
- {
- format_reader* Reader = p->Reader+No;
- filepos_t Limit = (Reader->OffsetMax - Reader->OffsetMin)/4;
- if (Limit<128*1024)
- Limit=128*1024;
- if (Limit>LimitMax)
- Limit=LimitMax;
- if (!Reader->OffsetMax ||
- (Reader->OffsetMin < Max && Reader->OffsetMax > Min &&
- abs(Reader->OffsetMin-Min)<Limit && abs(Reader->OffsetMax-Max)<Limit))
- {
- stream* Orig = p->Reader[0].Input;
- if (Reader->OffsetMin > Min)
- Reader->OffsetMin = Min;
- if (Reader->OffsetMax < Max)
- Reader->OffsetMax = Max;
- Reader->Ratio = Scale(MAX_INT,1,Reader->OffsetMax - Reader->OffsetMin);
- if (!Reader->Input && Orig)
- {
- bool_t Silent = 1;
- tchar_t URL[MAXPATH];
- stream* Input;
- if (Orig->Get(Orig,STREAM_URL,URL,sizeof(URL)) != ERR_NONE)
- break;
- Input = (stream*)NodeCreate(Orig->Class);
- if (!Input)
- break;
- Input->Set(Input,STREAM_SILENT,&Silent,sizeof(bool_t));
- if (Input->Set(Input,STREAM_URL,URL,sizeof(URL))!=ERR_NONE)
- {
- NodeDelete((node*)Input);
- break;
- }
- Reader->ReadPos = 0;
- Reader->FilePos = 0;
- Reader->Input = Input;
- }
- return Reader;
- }
- }
- return NULL;
- }
- static NOINLINE void Format_FreeStream(format_base* p,format_stream* Stream)
- {
- PacketFormatClear(&Stream->Format);
- BufferClear(&Stream->BufferMem);
- FreeBlock(&Stream->BufferBlock);
- free(Stream);
- }
- int Format_Reset(format_base* p)
- {
- int Result = ERR_NONE;
- format_packet* Packet;
- format_buffer* Buffer;
- format_reader* Reader;
- format_ref* Ref;
- int No;
- if (p->BufferLock) // only if it was inited
- {
- Format_AfterSeek(p);
- for (No=0;No<MAXREADER;++No)
- Format_ReleaseBuffers(p->Reader+No);
- }
- if (p->ReleaseStreams.Func)
- p->ReleaseStreams.Func(p->ReleaseStreams.This,0,0);
- if (p->Done)
- p->Done(p);
- while (p->StreamCount)
- Format_RemoveStream(p);
- while (p->FreePackets)
- {
- Packet = p->FreePackets;
- p->FreePackets = Packet->Next;
- free(Packet);
- }
- while (p->FreeBuffers)
- {
- Buffer = p->FreeBuffers;
- p->FreeBuffers = Buffer->Next;
- FreeBlock(&Buffer->Block);
- free(Buffer);
- }
- while (p->FreeRefs)
- {
- Ref = p->FreeRefs;
- p->FreeRefs = Ref->Next;
- free(Ref);
- }
- if (p->SubTitle)
- {
- Format_FreeSubTitle(p,p->SubTitle);
- p->SubTitle = NULL;
- }
- if (p->CoverArt)
- {
- Format_FreeStream(p,p->CoverArt);
- p->CoverArt = NULL;
- }
- p->TotalCount = 0;
- p->StreamCount = 0;
- p->ActiveStreams = 0;
- LockDelete(p->BufferLock);
- LockDelete(p->InputLock);
- p->BufferLock = NULL;
- p->InputLock = NULL;
- p->LastRead = NULL;
- for (No=0;No<MAXREADER;++No)
- {
- Reader = p->Reader+No;
- if (No>0 && Reader->Input)
- {
- Reader->Input->Set(Reader->Input,STREAM_URL,NULL,0);
- NodeDelete((node*)Reader->Input);
- Reader->Input = NULL;
- }
- Reader->Ratio = -1;
- Reader->OffsetMin = MAX_INT;
- Reader->OffsetMax = 0;
- Reader->Current = NULL;
- Reader->OutOfSync = 0;
- }
- Reader = p->Reader;
- if (Reader->Input)
- {
- if (!NodeIsClass(Reader->Input->Class,STREAM_CLASS) &&
- !NodeIsClass(Reader->Input->Class,STREAMPROCESS_CLASS))
- return ERR_INVALID_PARAM;
- if (Reader->Input->Get(Reader->Input,STREAM_LENGTH,&p->FileSize,sizeof(p->FileSize)) != ERR_NONE)
- p->FileSize = -1;
- p->InputLock = LockCreate();
- p->BufferLock = LockCreate();
- p->Duration = TIME_UNKNOWN;
- p->FirstSync = 1;
- p->SyncMode = 1; // start with syncmode
- p->SyncTime = TIME_SYNC;
- p->SyncStream = NULL;
- p->HeaderLoaded = 0;
- p->ProcessMinBuffer = PROCESSMINVIDEO;
- p->ReadSize = 16384;
- p->GlobalOffset = 0;
- p->SumByteRate = 0;
- Reader->FilePos = 0;
- Reader->NoMoreInput = 0;
- p->SeekByPacket_DataStart = 0;
- p->SeekByPacket_BlockAlign = 1;
- if (p->Init)
- {
- Result = p->Init(p);
- if (Result != ERR_NONE)
- {
- Reader->Input = NULL;
- Format_Reset(p);
- }
- else
- p->SyncRead = SYNCREAD;
- }
- }
- return Result;
- }
- int FormatBaseGet(format_base* p,int No,void* Data,int Size)
- {
- int Result = ERR_INVALID_PARAM;
- if (No >= FORMAT_COMMENT && No < FORMAT_COMMENT+p->TotalCount)
- GETVALUE(p->Streams[No-FORMAT_COMMENT]->Comment,pin)
- else
- if (No >= FORMAT_STREAM && No < FORMAT_STREAM+p->TotalCount)
- GETVALUE(p->Streams[No-FORMAT_STREAM]->Pin,pin)
- else
- if (No >= FORMAT_STREAM_PRIORITY && No < FORMAT_STREAM_PRIORITY+p->TotalCount)
- GETVALUE(p->Streams[No-FORMAT_STREAM_PRIORITY]->Priority,int)
- else
- if (No >= (FORMAT_STREAM|PIN_PROCESS) && No < (FORMAT_STREAM|PIN_PROCESS)+p->TotalCount)
- GETVALUE(p->Streams[No-(FORMAT_STREAM|PIN_PROCESS)]->Process,packetprocess)
- else
- if (No >= (FORMAT_STREAM|PIN_FORMAT) && No < (FORMAT_STREAM|PIN_FORMAT)+p->TotalCount)
- {
- packetformat CodecFormat;
- format_stream* s = p->Streams[No-(FORMAT_STREAM|PIN_FORMAT)];
- assert(Size == sizeof(packetformat));
- if (s->Pin.Node && s->Pin.Node->Get(s->Pin.Node,s->Pin.No|PIN_FORMAT,&CodecFormat,sizeof(CodecFormat))==ERR_NONE)
- PacketFormatCombine(&s->Format,&CodecFormat);
- *(packetformat*)Data = s->Format;
- Result = ERR_NONE;
- }
- else
- switch (No)
- {
- case FORMAT_BUFFERSIZE: GETVALUE(p->BufferSize,int); break;
- case FORMAT_INPUT: GETVALUE(p->Reader[0].Input,stream*); break;
- case FORMAT_DURATION: GETVALUECOND(p->Duration,tick_t,p->Duration>=0); break;
- case FORMAT_FILEPOS: GETVALUECOND(p->Reader[0].FilePos,int,p->Timing && p->Reader[0].FilePos>=0); break;
- case FORMAT_FILEALIGN: GETVALUE(p->FileAlign,int); break;
- case FORMAT_STREAM_COUNT: GETVALUE(p->TotalCount,int); break;
- case FORMAT_FILESIZE: GETVALUECOND(p->FileSize/1024,int,p->FileSize>=0); break;
- case FORMAT_FIND_SUBTITLES: GETVALUE(p->NeedSubtitles,bool_t); break;
- case FORMAT_GLOBAL_COMMENT: GETVALUE(p->Comment,pin); break;
- case FORMAT_AUTO_READSIZE: GETVALUE(p->AutoReadSize,bool_t); break;
- case FORMAT_TIMING: GETVALUE(p->Timing,bool_t); break;
- case FORMAT_COVERART: GETVALUECOND(p->StreamCount+(p->SubTitle!=NULL),int,p->CoverArt!=NULL); break;
- case FORMAT_MIN_BUFFER: GETVALUE(p->ProcessMinBuffer/BLOCKSIZE,int); break;
- }
- return Result;
- }
- int FormatBaseSet(format_base* p,int No,const void* Data,int Size)
- {
- node* Advanced;
- uint8_t* Ptr;
- int Result = ERR_INVALID_PARAM;
- if (No >= FORMAT_COMMENT && No < FORMAT_COMMENT+p->TotalCount)
- SETVALUE(p->Streams[No-FORMAT_COMMENT]->Comment,pin,ERR_NONE)
- else
- if (No >= FORMAT_STREAM && No < FORMAT_STREAM+p->TotalCount)
- {
- format_stream* s = p->Streams[No-FORMAT_STREAM];
- if (s->Pin.Node && No-FORMAT_STREAM<p->StreamCount) p->ActiveStreams--;
- SETVALUE(s->Pin,pin,UpdateStreamPin(p,s));
- if (s->Pin.Node && No-FORMAT_STREAM<p->StreamCount) p->ActiveStreams++;
- }
- else
- if (No >= (FORMAT_STREAM|PIN_PROCESS) && No < (FORMAT_STREAM|PIN_PROCESS)+p->TotalCount)
- SETVALUE(p->Streams[No-(FORMAT_STREAM|PIN_PROCESS)]->Process,packetprocess,ERR_NONE)
- else
- switch (No)
- {
- case FORMAT_INPUT: SETVALUENULL(p->Reader[0].Input,stream*,Format_Reset(p),p->Reader[0].Input=NULL); break;
- case FORMAT_UPDATESTREAMS: SETVALUE(p->UpdateStreams,notify,ERR_NONE); break;
- case FORMAT_RELEASESTREAMS: SETVALUE(p->ReleaseStreams,notify,ERR_NONE); break;
- case FORMAT_FIND_SUBTITLES: SETVALUE(p->NeedSubtitles,bool_t,ERR_NONE); break;
- case FORMAT_GLOBAL_COMMENT: SETVALUE(p->Comment,pin,ERR_NONE); break;
- case FORMAT_FILEALIGN: SETVALUE(p->FileAlign,int,ERR_NONE); break;
- case FORMAT_AUTO_READSIZE: SETVALUE(p->AutoReadSize,bool_t,ERR_NONE); break;
- case NODE_HIBERNATE:
- assert(Size == sizeof(int));
- Result = Format_Hibernate(p,*(int*)Data);
- break;
- case NODE_SETTINGSCHANGED:
- Advanced = Context()->Advanced;
- if (Advanced)
- {
- Advanced->Get(Advanced,ADVANCED_DROPTOL,&p->DropTolerance,sizeof(tick_t));
- Advanced->Get(Advanced,ADVANCED_SKIPTOL,&p->SkipTolerance,sizeof(tick_t));
- Advanced->Get(Advanced,ADVANCED_AVOFFSET,&p->AVOffset,sizeof(tick_t));
- }
- break;
- case FORMAT_DATAFEED:
- Ptr = (uint8_t*) Data;
- while (Size)
- {
- format_buffer* Buffer;
- Buffer = Format_BufferAlloc(p,2);
- if (Buffer)
- {
- Buffer->Length = Size;
- if (Buffer->Length > BLOCKSIZE)
- Buffer->Length = BLOCKSIZE;
- WriteBlock(&Buffer->Block,0,Ptr,Buffer->Length);
- Ptr += Buffer->Length;
- Size -= Buffer->Length;
- Format_BufferInsert(p->Reader,Buffer);
- }
- }
- break;
- case FORMAT_BUFFERSIZE: SETVALUE(p->BufferSize,int,ERR_NONE); break;
- }
- return Result;
- }
- format_packet* Format_PacketAlloc(format_base* p)
- {
- format_packet* Packet = p->FreePackets;
- if (!Packet)
- {
- Packet = (format_packet*) malloc(sizeof(format_packet));
- if (!Packet)
- return NULL;
- }
- else
- p->FreePackets = Packet->Next;
- Packet->Data = NULL;
- Packet->RefTime = TIME_UNKNOWN;
- Packet->EndRefTime = TIME_UNKNOWN;
- Packet->Stream = NULL;
- Packet->Next = NULL;
- Packet->Key = 1;
- return Packet;
- }
- static int Format_Sync(format_base* p,tick_t Time,int FilePos,bool_t PrevKey)
- {
- if (!p->Seek || !p->Reader[0].Input)
- return ERR_NOT_SUPPORTED;
- if (!p->HeaderLoaded)
- return ERR_LOADING_HEADER;
- p->SyncMode = 1;
- p->SyncRead = SYNCREAD;
- p->SyncTime = TIME_SYNC; // set before p->Seek
- p->SyncStream = NULL;
- if (Time>=0 || FilePos>=0)
- {
- int Result = p->Seek(p,Time,FilePos,PrevKey);
- if (Result != ERR_NONE)
- {
- p->SyncMode = 0;
- return Result;
- }
- }
- return ERR_NONE;
- }
- int Format_CheckEof(format_base* p,format_stream* Stream)
- {
- int Result;
- if (!p->Timing && p->ProcessTime>=0 && p->ProcessTime<5*TICKSPERSEC)
- return ERR_BUFFER_FULL;
- Stream->State.CurrTime = p->ProcessTime;
- Stream->State.DropLevel = 0;
- Result = Stream->Process(Stream->Pin.Node,NULL,&Stream->State);
- if (Result != ERR_BUFFER_FULL)
- Result = ERR_END_OF_FILE;
- return Result;
- }
- static NOINLINE int Synced(format_base* p,format_stream* Stream)
- {
- p->SyncMode = 0;
- if (p->SyncTime == TIME_SYNC)
- {
- p->SyncTime = Stream->Packet.RefTime;
- if (p->SyncTime < 0)
- p->SyncTime = Stream->LastTime;
- }
- if (p->FirstSync)
- {
- // some codecs may need the first few packets as headers
- int No;
- p->ProcessTime = 0;
- for (No=0;No<p->StreamCount;++No)
- if (p->Streams[No]->Pin.Node && p->Streams[No]->PacketFirst)
- p->Process(p,p->Streams[No]); // try processing
- p->ProcessTime = TIME_SYNC;
- }
- return ERR_SYNCED;
- }
- int Format_Send(format_base* p,format_stream* Stream)
- {
- int Result;
- tick_t CurrTime = p->ProcessTime;
- Stream->State.CurrTime = CurrTime;
- if (Stream->Packet.RefTime >= 0 && !Stream->DisableDrop)
- {
- Stream->State.DropLevel = 0;
- if (CurrTime >= 0)
- {
- tick_t Diff = CurrTime - Stream->Packet.RefTime;
- if (Diff < -TICKSPERSEC/2 && Stream->Format.Type == PACKET_VIDEO)
- {
- // make sure buffers and pending packets are processed
- Stream->Process(Stream->Pin.Node,NULL,&Stream->State);
- return ERR_BUFFER_FULL;
- }
- if (Diff > p->DropTolerance)
- {
- if (Diff > p->SkipTolerance + p->DropTolerance)
- {
- Stream->State.DropLevel = 2;
- Stream->DropCount = DROP_SHOW;
- }
- else
- {
- if (!Stream->State.DropLevel)
- Stream->DropCount = 0;
- if (Stream->DropCount >= DROP_SHOW)
- {
- Stream->State.DropLevel = 0; // show one packet
- Stream->DropCount = 0;
- }
- else
- {
- Stream->State.DropLevel = 1;
- ++Stream->DropCount;
- }
- }
- }
- }
- }
- DEBUG_MSG5(DEBUG_FORMAT,T("Send stream:%d size:%d time:%d curr:%d drop:%d"),Stream->No,Stream->Packet.Length,Stream->Packet.RefTime,CurrTime,Stream->State.DropLevel);
- Result = Stream->Process(Stream->Pin.Node,&Stream->Packet,&Stream->State);
- // packet not processed?
- if (Result == ERR_BUFFER_FULL)
- {
- DEBUG_MSG1(DEBUG_FORMAT,T("Send stream:%d BufferFull"),Stream->No);
- return Result;
- }
- if (p->Sended)
- p->Sended(p,Stream);
- Stream->Pending = 0;
- // sync: found a valid frame?
- if (p->SyncMode && (Result == ERR_NONE || Result == ERR_NOT_SUPPORTED))
- Result = Synced(p,Stream);
- return Result;
- }
- void Format_TimeStampRestarted(format_base* p,format_stream* Stream,tick_t* RefTime)
- {
- if (!p->InSeek)
- {
- format_packet* i;
- *RefTime -= p->GlobalOffset;
- p->GlobalOffset = Stream->LastTime; // last processed time
- for (i=Stream->PacketFirst;i;i=i->Next)
- if (i->RefTime>=0)
- p->GlobalOffset = i->RefTime; // last packet time
- *RefTime += p->GlobalOffset;
- p->TimeStamps = 0;
- }
- }
- int Format_FillQueue(format_base* p,format_reader* Reader)
- {
- int Result;
- format_packet* Packet;
- if (!Reader->BufferAvailable && !Reader->ReadBuffer && p->ProcessMinBuffer!=0 &&
- (!p->SyncMode || p->SyncRead<=0 || Reader->NoMoreInput))
- return ERR_NEED_MORE_DATA;
- Packet = Format_PacketAlloc(p);
- if (!Packet)
- return ERR_OUT_OF_MEMORY;
- Result = p->ReadPacket(p,Reader,Packet);
- if (Result == ERR_NONE && Packet->Stream && Packet->Stream->Pin.Node)
- {
- format_stream* s = Packet->Stream;
- if (Packet->RefTime>=0)
- {
- Packet->RefTime += p->GlobalOffset;
- if (s->Format.Type == PACKET_AUDIO)
- {
- Packet->RefTime += p->AVOffset;
- if (Packet->RefTime < 0)
- Packet->RefTime = 0;
- }
- if (s->LastTime<0)
- s->LastTime = Packet->RefTime; // found reftime
- else
- if (s->LastTime > Packet->RefTime+TICKSPERSEC && Packet->RefTime-p->GlobalOffset<TICKSPERSEC)
- Format_TimeStampRestarted(p,s,&Packet->RefTime);
- }
- if (s->LastTime >= TIME_UNKNOWN)
- {
- // add packet to stream
- if (!s->PacketFirst)
- s->PacketFirst = Packet;
- else
- s->PacketLast->Next = Packet;
- s->PacketLast = Packet;
- if (Packet->EndRefTime>=0 && s->Format.Type == PACKET_SUBTITLE)
- {
- // add empty packet
- format_packet* Tail = Format_PacketAlloc(p);
- if (Tail)
- {
- Tail->Stream = Packet->Stream;
- Tail->RefTime = Packet->EndRefTime;
- if (!s->PacketFirst)
- s->PacketFirst = Tail;
- else
- s->PacketLast->Next = Tail;
- s->PacketLast = Tail;
- }
- }
- }
- else
- Format_PacketRelease(p,Packet);
- }
- else
- Format_PacketRelease(p,Packet);
- return Result;
- }
- NOINLINE bool_t Format_AllocBufferBlock(format_base* p,format_stream* Stream,int Size)
- {
- FreeBlock(&Stream->BufferBlock);
- if (Size>0)
- {
- Size = (Size + SAFETAIL + 0x7FFF) & ~0x7FFF;
- if (AllocBlock(Size,&Stream->BufferBlock,0,HEAP_STORAGE))
- Size -= SAFETAIL;
- else
- Size = 0;
- }
- Stream->BufferBlockLength = Size;
- return Size>0;
- }
- /*
- static bool_t ReleasePacket(format_base* p)
- {
- int No;
- format_stream* Min = NULL;
- tick_t MinTime = MAX_TICK;
- for (No=0;No<p->StreamCount;++No)
- {
- format_stream* Stream = p->Streams[No];
- if (Stream->PacketFirst && Stream->PacketFirst->RefTime < MinTime)
- {
- Min = Stream;
- MinTime = Stream->PacketFirst->RefTime;
- }
- }
- if (Min)
- {
- format_packet* Packet = Min->PacketFirst;
- Min->PacketFirst = Packet->Next;
- if (Min->PacketLast == Packet)
- Min->PacketLast = NULL;
- Format_PacketRelease(p,Packet);
- return 1;
- }
- return 0;
- }
- */
- static int Format_ProcessStream(format_base* p,format_stream* Stream)
- {
- format_packet* Packet;
- format_ref* Ref;
- int Result = ERR_NONE;
- int No;
- int Burst;
- if (Stream->Pending)
- {
- Result = Format_Send(p,Stream);
- if (Result == ERR_BUFFER_FULL || Result == ERR_SYNCED)
- {
- if (p->SyncMode && Stream == p->SyncStream) // can happen when sync without seek
- Result = Synced(p,Stream);
- return Result;
- }
- }
- // process a limited number of packets at once (other streams need cpu time too)
- Burst = Stream->PacketBurst;
- for (No=0;No<Burst;++No)
- {
- while (!Stream->PacketFirst)
- {
- format_reader* Reader = Stream->Reader;
- /*
- if (p->BufferUsed >= p->BufferSize && p->SyncMode && Stream == p->SyncStream)
- {
- // try dropping other stream packets to make buffer for sync stream
- while (p->BufferUsed >= p->BufferSize)
- if (!ReleasePacket(p))
- break;
- }
- */
- // prevent audio from going too much ahead
- if (Reader->BufferAvailable < p->ProcessMinBuffer && !Reader->NoMoreInput &&
- (!p->SyncMode || p->SyncRead<=0 || Stream != p->SyncStream))
- return ERR_NEED_MORE_DATA;
- Result = Format_FillQueue(p,Reader);
- if (Reader->NoMoreInput && (Result == ERR_END_OF_FILE || Result == ERR_NEED_MORE_DATA))
- Result = Format_CheckEof(p,Stream);
- if (Result != ERR_NONE || (p->Bench && (No || !Stream->PacketFirst)))
- return Result;
- }
- Packet = Stream->PacketFirst;
- // sync: all non SyncStream packets are stored (or dropped)
- if (p->SyncMode && Stream != p->SyncStream && p->SyncStream)
- {
- // catch up to syncstream (drop packets)
- tick_t LastTime = p->SyncStream->LastTime;
- if (p->SyncStream->Fragmented)
- LastTime -= TICKSPERSEC/2; // LastTime may not be the correct sync time
- while (Packet && Packet->RefTime < LastTime)
- {
- Stream->PacketFirst = Packet->Next;
- if (Stream->PacketLast == Packet)
- Stream->PacketLast = NULL;
- Format_PacketRelease(p,Packet);
- Packet = Stream->PacketFirst;
- }
- return ERR_NEED_MORE_DATA; // allow SyncStream to need more data
- }
- //DEBUG_MSG3(DEBUG_FORMAT,T("Packet stream:%d time:%d pos:%d"),Stream->No,Packet->RefTime,Packet->FilePos);
- // build output packet
- if (Packet->RefTime >= 0)
- Stream->LastTime = Packet->RefTime;
- Stream->Packet.RefTime = Packet->RefTime;
- Stream->Packet.Key = Packet->Key;
- Ref = Packet->Data;
- if (!Ref)
- {
- Stream->Packet.Data[0] = NULL;
- Stream->Packet.Length = 0;
- Stream->Pending = 1;
- }
- else
- if (!Stream->Fragmented)
- {
- if (Ref->Next || (Ref->Length + Ref->Begin + SAFETAIL > BLOCKSIZE) || Stream->ForceMerge)
- {
- // merge references
- if (p->UseBufferBlock)
- {
- int Total = 0;
- format_ref* i;
- for (Total=0,i=Ref;i;i=i->Next)
- Total += i->Length;
- if (Stream->BufferBlockLength < Total && !Format_AllocBufferBlock(p,Stream,Total))
- Ref = NULL;
- for (Total=0,i=Ref;i;i=i->Next)
- {
- WriteBlock(&Stream->BufferBlock,Total,i->Buffer->Block.Ptr + i->Begin,i->Length);
- Total += i->Length;
- }
- Stream->Packet.Data[0] = Stream->BufferBlock.Ptr;
- Stream->Packet.Length = Total;
- }
- else
- {
- BufferDrop(&Stream->BufferMem);
- for (;Ref;Ref=Ref->Next)
- BufferWrite(&Stream->BufferMem,Ref->Buffer->Block.Ptr + Ref->Begin, Ref->Length,16384);
- Stream->Packet.Data[0] = Stream->BufferMem.Data;
- Stream->Packet.Length = Stream->BufferMem.WritePos;
- }
- }
- else
- {
- // single reference
- Stream->Packet.Data[0] = Ref->Buffer->Block.Ptr + Ref->Begin;
- Stream->Packet.Length = Ref->Length;
- }
- Stream->Pending = 1;
- }
- else
- {
- // send first reference (doesn't matter how long)
- Stream->Packet.Data[0] = Ref->Buffer->Block.Ptr + Ref->Begin;
- Stream->Packet.Length = Ref->Length;
- Stream->Pending = 2; // indicate parital sending
- }
- Result = Format_Send(p,Stream);
- if (Result == ERR_BUFFER_FULL || Result == ERR_SYNCED)
- break;
- if (Stream->State.DropLevel==2 && Stream->PacketFirst && Burst<10 && No==Burst-1)
- ++Burst; // try to catch up
- }
- // buffer full: there was possibly some processing so don't allow sleeping
- // need data: burst count exceeded, but it doesn't mean there is no data left in buffers
- if (Result == ERR_BUFFER_FULL || Result == ERR_NEED_MORE_DATA)
- Result = ERR_NONE;
- return Result;
- }
- static void Format_Sended(format_base* p, format_stream* Stream)
- {
- format_packet* Packet = Stream->PacketFirst;
- if (Packet)
- {
- if (Stream->Pending == 2 && Packet->Data)
- {
- // release sended references
- format_ref* Ref = Packet->Data;
- Packet->Data = Ref->Next;
- Format_SingleRefRelease(p,Ref);
- if (Packet->Data)
- {
- // packet still not finished
- Packet->RefTime = TIME_UNKNOWN;
- return;
- }
- }
- // full packet is released
- Stream->PacketFirst = Packet->Next;
- if (Stream->PacketLast == Packet)
- Stream->PacketLast = NULL;
- Format_PacketRelease(p,Packet);
- }
- }
- NOINLINE format_stream* Format_DefSyncStream(format_base* p, format_reader* Reader)
- {
- format_stream* Found = NULL;
- format_stream* Stream;
- int No;
- // prefer video streams
- for (No=0;No<p->StreamCount;++No)
- {
- Stream = p->Streams[No];
- if (Stream->Pin.Node && (!Reader || Stream->Reader==Reader))
- {
- if (Stream->Format.Type == PACKET_VIDEO)
- return Stream;
- if (!Found)
- Found = Stream;
- }
- }
- return Found;
- }
- static bool_t TryReSend(node* p)
- {
- if (p)
- {
- bool_t Buffered;
- if (p->Get(p,FLOW_BUFFERED,&Buffered,sizeof(Buffered))==ERR_NONE && Buffered)
- return p->Set(p,FLOW_RESEND,NULL,0) == ERR_NONE;
- if (!NodeIsClass(p->Class,OUT_CLASS))
- {
- pin Pin;
- node* Node;
- datadef DataDef;
- int No;
- for (No=0;NodeEnum(p,No,&DataDef)==ERR_NONE;++No)
- if (DataDef.Flags & DF_OUTPUT)
- {
- switch (DataDef.Type)
- {
- case TYPE_NODE:
- if (p->Get(p,DataDef.No,&Node,sizeof(Node))==ERR_NONE && TryReSend(Node))
- return 1;
- break;
- case TYPE_PACKET:
- if (p->Get(p,DataDef.No,&Pin,sizeof(Pin))==ERR_NONE && TryReSend(Pin.Node))
- return 1;
- break;
- }
- }
- }
- }
- return 0;
- }
- static int DummyError(void* This,int Param,int Param2)
- {
- return ERR_NONE;
- }
- static NOINLINE int ProcessCoverArt(format_base* p,format_stream* Stream)
- {
- int Result;
- notify Error;
- context* c = Context();
- node* Node = Stream->Pin.Node;
- if (!Node)
- return ERR_NONE;
- if (TryReSend(Node))
- return ERR_NONE;
- // suppress covert art decoding error messages
- Error = c->Error;
- c->Error.Func = DummyError;
- Stream->State.CurrTime = TIME_SYNC;
- Stream->State.DropLevel = 0;
- Result = Stream->Process(Node,&Stream->Packet,&Stream->State);
- c->Error = Error;
- return Result;
- }
- static int Format_Process(format_base* p,processstate* State)
- {
- int No;
- int Result;
- int AllNeedMoreData = 1;
- int AllEndOfFile = 1;
- assert(State->Time>=0 || State->Time == TIME_BENCH);
- State->BufferUsedBefore = p->BufferUsed;
- p->ProcessTime = State->Time;
- p->Bench = p->ProcessTime < 0;
- if (!p->HeaderLoaded)
- {
- // load format begining to find streams
- format_reader* Reader = p->Reader;
- Result = p->FillQueue(p,Reader);
- if (Reader->FilePos > HEADERLOAD || (p->StreamCount && Reader->FilePos > p->MinHeaderLoad) || Reader->Eof(Reader))
- p->HeaderLoaded = 1;
- State->BufferUsedAfter = p->BufferUsed;
- return Result;
- }
- if (p->SyncMode)
- {
- p->ProcessTime = TIME_SYNC;
- // we need to choose one stream (to find a non dropped frame)
- if (!p->SyncStream)
- {
- p->SyncStream = Format_DefSyncStream(p,NULL);
- if (!p->SyncStream)
- {
- // no sync needed
- p->SyncMode = 0;
- // try to calc duration once (after first sync)
- if (p->FirstSync)
- {
- p->FirstSync = 0;
- CalcDuration(p);
- }
- if (p->CoverArt)
- ProcessCoverArt(p,p->CoverArt);
- // leave time as it is
- State->BufferUsedAfter = p->BufferUsed;
- return ERR_SYNCED;
- }
- }
- }
- // if all streams are full: caller can go to sleep
- Result = ERR_BUFFER_FULL;
- if (p->SubTitle)
- Format_ProcessSubTitle(p,p->SubTitle);
- for (No=0;No<p->StreamCount;++No)
- {
- format_stream* Stream = p->Streams[No];
- if (Stream->Pin.Node)
- {
- int i = p->Process(p,Stream);
- if (!p->SyncMode || p->SyncStream == Stream)
- {
- if (i == ERR_NEED_MORE_DATA)
- {
- if (!Stream->Reader->NoMoreInput && Stream->Format.Type != PACKET_SUBTITLE)
- {
- // if less then 1/8 sec is left and stream needs data -> force need more data mode
- tick_t LastTime = Stream->LastTime;
- if (State->Fill || (LastTime >= 0 && p->ProcessTime >= 0 && LastTime < p->ProcessTime + TICKSPERSEC/8))
- Result = ERR_NEED_MORE_DATA;
- }
- }
- else
- AllNeedMoreData = 0;
- if (i != ERR_END_OF_FILE)
- AllEndOfFile = 0;
- }
- if (i == ERR_SYNCED)
- {
- // try to calc duration once (after first sync)
- if (p->FirstSync)
- {
- p->FirstSync = 0;
- CalcDuration(p);
- }
- if (p->CoverArt)
- ProcessCoverArt(p,p->CoverArt);
- State->Time = p->SyncTime;
- State->BufferUsedAfter = p->BufferUsed;
- return i;
- }
- if (Result == ERR_BUFFER_FULL &&
- i != ERR_BUFFER_FULL &&
- i != ERR_END_OF_FILE &&
- i != ERR_NEED_MORE_DATA)
- Result = ERR_NONE; // no sleep
- if (Result == ERR_NONE && Stream->State.DropLevel)
- Result = ERR_DROPPING;
- }
- }
- if (!p->ActiveStreams)
- {
- // release buffers
- Result = ERR_END_OF_FILE;
- for (No=0;No<MAXREADER;++No)
- {
- format_buffer* Buffer;
- format_reader* Reader = p->Reader+No;
- if (!Reader->Input)
- break;
- while ((Buffer = Format_BufferRemove(Reader))!=NULL)
- {
- Reader->FilePos += Buffer->Length;
- Format_BufferRelease(p,Buffer);
- }
- if (!Reader->NoMoreInput)
- Result = ERR_BUFFER_FULL; // return buffer full so fill mode doesn't go though file
- }
- State->Time = TIME_UNKNOWN; // player should use file position
- }
- else
- {
- if (AllEndOfFile) // all end of file?
- Result = ERR_END_OF_FILE;
- else
- if (AllNeedMoreData) // all need data?
- {
- Result = ERR_END_OF_FILE;
- for (No=0;No<MAXREADER;++No)
- {
- if (!p->Reader[No].Input)
- break;
- if (!p->Reader[No].NoMoreInput)
- {
- Result = ERR_NEED_MORE_DATA;
- break;
- }
- }
- }
- else
- if (Result == ERR_BUFFER_FULL && State->Fill)
- {
- // don't allow exiting fill mode if buffer is less than ProcessMinBuffer+BLOCKSIZE
- // we might fallback to loadmode right after starting playback
- if (p->Reader->BufferAvailable < p->ProcessMinBuffer+BLOCKSIZE && !p->Reader->NoMoreInput)
- Result = ERR_NEED_MORE_DATA;
- }
- }
- if (p->SyncMode && Result == ERR_END_OF_FILE)
- {
- p->SyncMode = 0;
- if (p->Duration<0 && p->SyncStream)
- State->Time = p->SyncStream->LastTime;
- else
- State->Time = p->Duration;
- Result = ERR_SYNCED;
- }
- State->BufferUsedAfter = p->BufferUsed;
- return Result;
- }
- format_ref* Format_RefAlloc(format_base* p, format_buffer* To, int Begin, int Length)
- {
- format_ref* Ref = p->FreeRefs;
- if (!Ref)
- {
- Ref = (format_ref*) malloc(sizeof(format_ref));
- if (!Ref)
- return NULL;
- }
- else
- p->FreeRefs = Ref->Next;
- LockEnter(p->BufferLock);
- //DEBUG_MSG3(DEBUG_FORMAT,"Buffer:%d addref %d->%d",To->Id,To->RefCount,To->RefCount+1);
- ++To->RefCount;
- LockLeave(p->BufferLock);
- Ref->Length = Length;
- Ref->Begin = Begin;
- Ref->Buffer = To;
- Ref->Next = NULL;
- return Ref;
- }
- bool_t Format_ReadBuffer(format_reader* Reader, bool_t ToRead)
- {
- bool_t Result;
- LockEnter(Reader->Format->InputLock);
- LockEnter(Reader->Format->BufferLock);
- if (!Reader->BufferFirst && !Reader->NoMoreInput)
- {
- format_buffer* Buffer = Reader->InputBuffer;
- if (!Buffer)
- Buffer = Format_BufferAlloc(Reader->Format,1);
- if (Buffer)
- {
- // use partial buffer loading after sync for a few times (SYNCREAD)
- // but later have to use full buffer (matroska seeking example can run out of buffer...)
- int TargetLength = (ToRead && Reader->Format->SyncRead>0)? Reader->Format->ReadSize:BLOCKSIZE;
- if (Buffer->Length >= TargetLength)
- {
- Format_BufferInsert(Reader,Buffer);
- Reader->InputBuffer = NULL;
- }
- else
- {
- int Length = Reader->Input->ReadBlock(Reader->Input,&Buffer->Block,Buffer->Length,TargetLength - Buffer->Length);
- if (Length < 0)
- {
- // failed
- if (!Reader->InputBuffer)
- Format_BufferRelease(Reader->Format,Buffer);
- }
- else
- {
- Buffer->Length += Length;
- Reader->NoMoreInput = Length == 0;
- if (!Length && !Reader->InputBuffer)
- Format_BufferRelease(Reader->Format,Buffer);
- else
- {
- Format_BufferInsert(Reader,Buffer);
- Reader->InputBuffer = NULL;
- }
- }
- }
- }
- if (Reader->Format->SyncRead>0)
- Reader->Format->SyncRead--;
- }
- Result = Reader->BufferFirst != NULL;
- if (Result && ToRead)
- {
- Reader->ReadBuffer = Format_BufferRemove(Reader);
- if (Reader->ReadBuffer)
- Reader->ReadLen = Reader->ReadBuffer->Length;
- }
- LockLeave(Reader->Format->BufferLock);
- LockLeave(Reader->Format->InputLock);
- return Result;
- }
- format_ref* Format_DupRef(format_base* p, format_ref* Chain, int Offset, int Length)
- {
- format_ref* Head = NULL;
- format_ref** Ptr = &Head;
- while (Chain && Offset >= Chain->Length)
- {
- Chain = Chain->Next;
- Offset -= Chain->Length;
- }
- while (Chain && Length > 0)
- {
- int Len = Chain->Length - Offset;
- if (Len > Length)
- Len = Length;
- *Ptr = Format_RefAlloc(p,Chain->Buffer,Chain->Begin+Offset,Len);
- if (!*Ptr)
- break;
- Chain = Chain->Next;
- Length -= Len;
- Offset = 0;
- Ptr = &(*Ptr)->Next;
- }
- return Head;
- }
- //---------------------------------------------------------------------------------------------
- static bool_t Reader_GetReadBuffer(format_reader* Reader)
- {
- if (!Reader->Input) // external reader
- {
- Reader->ReadBuffer = NULL;
- return 0;
- }
- // release old buffer
- if (Reader->ReadBuffer)
- {
- Reader->ReadPos -= Reader->ReadBuffer->Length;
- Format_BufferRelease(Reader->Format,Reader->ReadBuffer);
- }
- // get new buffer from BufferFirst..BufferLast chain
- Reader->ReadBuffer = Format_BufferRemove(Reader);
- if (Reader->ReadBuffer)
- {
- Reader->ReadLen = Reader->ReadBuffer->Length;
- return 1;
- }
- // load input now (can't wait for input thread)
- return Format_ReadBuffer(Reader,1);
- }
- format_ref* Reader_ReadAsRef(format_reader* p, int Length)
- {
- int n;
- format_ref* Ref;
- format_ref** RefPtr;
- Ref = NULL;
- RefPtr = &Ref;
- if (Length < 0) // read as much as possible from one buffer
- {
- while (!p->ReadBuffer || p->ReadPos >= p->ReadLen)
- if (!p->GetReadBuffer(p))
- return NULL;
- n = p->ReadLen - p->ReadPos;
- if (n > -Length)
- n = -Length;
- Length = n;
- }
- while (Length > 0)
- {
- while (!p->ReadBuffer || p->ReadPos >= p->ReadLen)
- if (!p->GetReadBuffer(p))
- return Ref;
- n = p->ReadLen - p->ReadPos;
- if (n > Length)
- n = Length;
- *RefPtr = Format_RefAlloc(p->Format,p->ReadBuffer,p->ReadPos,n);
- if (!*RefPtr)
- break;
- RefPtr = &(*RefPtr)->Next;
- Length -= n;
- p->FilePos += n;
- p->ReadPos += n;
- }
- return Ref;
- }
- static void Reader_Skip(format_reader* p,int n)
- {
- if (n > 0)
- {
- assert(n < 0x10000000);
- p->FilePos += n;
- p->ReadPos += n;
- }
- }
- static int Reader_Seek(format_reader* Reader,filepos_t Pos,int Origin)
- {
- int Adjust = 0;
- if (Reader->FilePos >= 0 && !Reader->Format->DisableReader)
- {
- filepos_t RelPos = Pos;
- if (Origin == SEEK_SET)
- RelPos -= Reader->FilePos;
- if (Origin != SEEK_END)
- {
- // check if we can use Format_Skip
- if (RelPos >= 0 && RelPos < Reader->BufferAvailable + SKIPDISTANCE)
- {
- Reader_Skip(Reader,RelPos);
- return ERR_NONE;
- }
- // check if we are in the same ReadBuffer
- if (RelPos < 0 && Reader->ReadBuffer && Reader->ReadPos + RelPos >= 0)
- {
- Reader->FilePos += RelPos;
- Reader->ReadPos += RelPos;
- return ERR_NONE;
- }
- }
- }
- if (!Reader->Input)
- return ERR_NOT_SUPPORTED;
- if (Origin == SEEK_CUR)
- {
- // input is at a different position already, using SEEK_SET
- Origin = SEEK_SET;
- Pos += Reader->FilePos;
- }
- if (Origin == SEEK_SET)
- {
- // align reading for local files
- Adjust = Pos & (Reader->Format->FileAlign-1);
- Pos &= ~(Reader->Format->FileAlign-1);
- }
- LockEnter(Reader->Format->InputLock);
- Pos = Reader->Input->Seek(Reader->Input,Pos,Origin);
- if (Pos < 0)
- {
- LockLeave(Reader->Format->InputLock);
- return ERR_NOT_SUPPORTED;
- }
- // query again file size, because http could have reopened connection
- if (Reader->Input->Get(Reader->Input,STREAM_LENGTH,&Reader->Format->FileSize,sizeof(Reader->Format->FileSize)) != ERR_NONE)
- Reader->Format->FileSize = -1;
- Format_ReleaseBuffers(Reader);
- Reader->FilePos = Pos + Adjust;
- Reader->ReadPos += Adjust;
- LockLeave(Reader->Format->InputLock);
- return ERR_NONE;
- }
- static bool_t Reader_Eof(format_reader* p)
- {
- return !p->BufferAvailable && !p->ReadBuffer && p->NoMoreInput;
- }
- static int Reader_Read(format_reader* p, void* Data, int Length0)
- {
- uint8_t* Ptr = (uint8_t*)Data;
- int Length = Length0;
- int n;
- while (Length > 0)
- {
- while (!p->ReadBuffer || p->ReadPos >= p->ReadLen)
- if (!p->GetReadBuffer(p))
- return Length0 - Length;
- n = p->ReadLen - p->ReadPos;
- if (n > Length)
- n = Length;
- memcpy(Ptr,p->ReadBuffer->Block.Ptr+p->ReadPos,n);
- Ptr += n;
- Length -= n;
- p->FilePos += n;
- p->ReadPos += n;
- }
- return Length0;
- }
- static int Reader_Read8(format_reader* p)
- {
- while (!p->ReadBuffer || p->ReadPos >= p->ReadLen)
- if (!p->GetReadBuffer(p))
- return -1;
- p->FilePos++;
- return p->ReadBuffer->Block.Ptr[p->ReadPos++];
- }
- static int Reader_ReadBE16(format_reader* p)
- {
- int v = Reader_Read8(p) << 8;
- v |= Reader_Read8(p);
- return v;
- }
- static int Reader_ReadLE16(format_reader* p)
- {
- int v = Reader_Read8(p);
- v |= Reader_Read8(p) << 8;
- return v;
- }
- static int Reader_ReadBE32(format_reader* p)
- {
- int v = Reader_Read8(p) << 24;
- v |= Reader_Read8(p) << 16;
- v |= Reader_Read8(p) << 8;
- v |= Reader_Read8(p);
- return v;
- }
- static int Reader_ReadLE32(format_reader* p)
- {
- int v = Reader_Read8(p);
- v |= Reader_Read8(p) << 8;
- v |= Reader_Read8(p) << 16;
- v |= Reader_Read8(p) << 24;
- return v;
- }
- static int64_t Reader_ReadBE64(format_reader* p)
- {
- int64_t v = (int64_t)Reader_ReadBE32(p) << 32;
- v |= (uint32_t)Reader_ReadBE32(p);
- return v;
- }
- static int64_t Reader_ReadLE64(format_reader* p)
- {
- int64_t v = (uint32_t)Reader_ReadLE32(p);
- v |= (int64_t)Reader_ReadLE32(p) << 32;
- return v;
- }
- static NOINLINE int GetRate(format_reader* p,format_reader* Last)
- {
- int64_t Rate;
- if (p->NoMoreInput)
- return MAX_INT;
- if (p->BufferAvailable <= PROCESSMINVIDEO)
- return -MAX_INT;
- Rate = (int64_t)p->Ratio * (p->BufferAvailable - (p==Last?READER_BONUS:0));
- if (Rate > MAX_INT)
- return MAX_INT;
- return (int)Rate;
- }
- static int Format_ReadInput(format_base* p,int BufferMax,int* BufferUsed)
- {
- int No;
- int Left,Length;
- int Result = ERR_NONE;
- format_buffer* Buffer;
- format_reader* Reader;
- int Used = p->BufferUsed;
- // simple estimate test without locking BufferUsed
- if (Used >= BufferMax)
- {
- *BufferUsed = Used;
- return ERR_BUFFER_FULL;
- }
- LockEnter(p->InputLock);
- LockEnter(p->BufferLock);
- // select which reader to use
- Reader = p->Reader;
- if (p->Reader[1].Input)
- {
- int Lowest = GetRate(Reader,p->LastRead);
- for (No=1;No<MAXREADER;++No)
- {
- format_reader* r = p->Reader+No;
- if (r->Ratio > 0)
- {
- int Rate = GetRate(r,p->LastRead);
- if (Lowest > Rate)
- {
- Lowest = Rate;
- Reader = r;
- }
- }
- }
- p->LastRead = Reader;
- }
- Buffer = Reader->InputBuffer;
- if (!Buffer)
- {
- // allocate input buffer
- if (p->BufferUsed < BufferMax) // check again (BufferUsed is locked)
- {
- Buffer = Format_BufferAlloc(p,0);
- if (p->BufferUsed >= BufferMax)
- Result = ERR_BUFFER_FULL;
- }
- if (!Buffer)
- {
- *BufferUsed = p->BufferUsed;
- LockLeave(p->BufferLock);
- LockLeave(p->InputLock);
- return ERR_BUFFER_FULL;
- }
- #ifndef NDEBUG
- Buffer->FilePos = Reader->Input->Seek(Reader->Input,0,SEEK_CUR);
- #endif
- }
- *BufferUsed = p->BufferUsed;
- LockLeave(p->BufferLock);
- Left = BLOCKSIZE - Buffer->Length;
- if (Left > p->ReadSize)
- Left = p->ReadSize;
- if (Reader->Input->DataAvailable)
- {
- int Available = Reader->Input->DataAvailable(Reader->Input);
- if (Available>=0)
- {
- if (!Available)
- {
- if (!Reader->InputBuffer)
- Format_BufferRelease(Reader->Format,Buffer);
- LockLeave(p->InputLock);
- return ERR_NEED_MORE_DATA;
- }
- if (Left > Available)
- Left = Available;
- }
- }
- Length = Reader->Input->ReadBlock(Reader->Input,&Buffer->Block,Buffer->Length,Left);
- if (Length < 0)
- {
- // failed
- if (!Reader->InputBuffer)
- Format_BufferRelease(Reader->Format,Buffer);
- Result = ERR_DEVICE_ERROR;
- }
- else
- {
- Buffer->Length += Length;
- Reader->NoMoreInput = Length == 0;
- if (!Length)
- {
- // only return end of file if all readers are at the end
- Result = ERR_END_OF_FILE;
- for (No=0;No<MAXREADER;++No)
- {
- if (!p->Reader[No].Input)
- break;
- if (!p->Reader[No].NoMoreInput)
- {
- Result = ERR_NONE;
- break;
- }
- }
- }
- if (!Length && !Reader->InputBuffer)
- Format_BufferRelease(Reader->Format,Buffer);
- else
- {
- if (!Length || Buffer->Length == BLOCKSIZE || p->ProcessMinBuffer==0)
- {
- LockEnter(p->BufferLock);
- Format_BufferInsert(Reader,Buffer);
- LockLeave(p->BufferLock);
- Buffer = NULL;
- }
- Reader->InputBuffer = Buffer;
- }
- }
- LockLeave(p->InputLock);
- return Result;
- }
- static NOINLINE void UpdateTotalCount(format_base* p)
- {
- p->TotalCount = p->StreamCount;
- if (p->SubTitle)
- p->Streams[p->TotalCount++] = p->SubTitle;
- if (p->CoverArt)
- p->Streams[p->TotalCount++] = p->CoverArt;
- }
- static NOINLINE void ReadCoverArt(format_base* p,stream* Input,filepos_t Pos,int Len,const tchar_t* ContentType,const tchar_t* URL)
- {
- uint8_t Probe[512];
- int ProbeLen;
- Input->Seek(Input,Pos,SEEK_SET);
- ProbeLen = Input->Read(Input,Probe,min(Len,sizeof(Probe)));
- if (ProbeLen>0)
- {
- int *i;
- array List;
- NodeEnumClassEx(&List,RAWIMAGE_CLASS,ContentType,URL,Probe,ProbeLen);
- for (i=ARRAYBEGIN(List,int);i!=ARRAYEND(List,int);++i)
- {
- const tchar_t* Format = LangStr(*i,RAWIMAGE_FORMAT);
- if (tcsnicmp(Format,T("vcodec/"),7)==0)
- {
- format_stream* s = (format_stream*) malloc(sizeof(format_stream));
- if (!s)
- return;
- memset(s,0,sizeof(format_stream));
- s->Format.Type = PACKET_VIDEO;
- s->Format.Format.Video.Pixel.Flags = PF_FOURCC|PF_NOPREROTATE;
- s->Format.Format.Video.Pixel.FourCC = StringToFourCC(Format+7,1);
- if (Format_AllocBufferBlock(p,s,Len))
- {
- WriteBlock(&s->BufferBlock,0,Probe,ProbeLen);
- s->Packet.Length = ProbeLen;
- if (Len>ProbeLen)
- s->Packet.Length += Input->ReadBlock(Input,&s->BufferBlock,ProbeLen,Len-ProbeLen);
- }
- if (s->Packet.Length>0)
- {
- s->Packet.Data[0] = s->BufferBlock.Ptr;
- s->Packet.Key = 1;
- s->Packet.RefTime = 0;
- p->CoverArt = s;
- UpdateTotalCount(p);
- Format_PrepairStream(p,s);
- }
- else
- Format_FreeStream(p,s);
- break;
- }
- }
- ArrayClear(&List);
- }
- }
- static NOINLINE bool_t GetNonStreamingURL(format_base* p,tchar_t* URL,int URLLen,bool_t Local)
- {
- bool_t HasHost;
- int Length;
- stream* Input = p->Reader->Input;
- if (!Input)
- return 0;
- LockEnter(p->InputLock);
- if (Input->Get(Input,STREAM_URL,URL,URLLen*sizeof(tchar_t)) != ERR_NONE)
- URL[0] = 0;
- if (Input->Get(Input,STREAM_LENGTH,&Length,sizeof(Length)) != ERR_NONE)
- Length = -1;
- LockLeave(p->InputLock);
- if (!URL[0])
- return 0; //no URL?
- GetMime(URL,NULL,0,&HasHost);
- if (HasHost && (Local || Length<0))
- return 0; //non local or streaming
- return 1;
- }
- static NOINLINE bool_t OpenCoverAtr(format_base* p, const tchar_t* Base, const tchar_t* Name)
- {
- tchar_t Path[MAXPATH];
- int Length;
- stream* Input;
- AbsPath(Path,TSIZEOF(Path),Name,Base);
- if (FileExits(Path) && (Input = GetStream(Path,1))!=NULL)
- {
- //todo: cache global cover art...
- if (Input->Set(Input,STREAM_URL,Path,sizeof(tchar_t)*(tcslen(Path)+1)) == ERR_NONE &&
- Input->Get(Input,STREAM_LENGTH,&Length,sizeof(Length)) == ERR_NONE)
- ReadCoverArt(p,Input,0,Length,NULL,Path);
- Input->Set(Input,STREAM_URL,NULL,0);
- NodeDelete((node*)Input);
- }
- return p->CoverArt != NULL;
- }
- static NOINLINE void FindCoverArt(format_base* p)
- {
- tchar_t Value[MAXPATH];
- if (p->Comment.Node && NodeIsClass(p->Comment.Node->Class,PLAYER_ID) &&
- ((player*)p->Comment.Node)->CommentByName(p->Comment.Node,-1,PlayerComment(COMMENT_COVER),Value,TSIZEOF(Value)))
- {
- tchar_t* SPos = tcschr(Value,':');
- tchar_t* SLen = SPos?tcschr(SPos+1,':'):NULL;
- tchar_t* ContentType = SLen?tcschr(SLen+1,':'):NULL;
- int Pos = SPos ? StringToInt(SPos+1,0):0;
- int Len = SLen ? StringToInt(SLen+1,0):0;
- if (ContentType && Len>0)
- {
- stream* Input = p->Reader->Input;
- filepos_t SavePos;
- LockEnter(p->InputLock);
- SavePos = Input->Seek(Input,0,SEEK_CUR);
- if (SavePos >= 0)
- {
- if (*(++ContentType)==0)
- ContentType = NULL;
- ReadCoverArt(p,Input,Pos,Len,ContentType,NULL);
- Input->Seek(Input,SavePos,SEEK_SET);
- }
- LockLeave(p->InputLock);
- }
- }
- else if (NodeIsClass(p->Format.Class,RAWAUDIO_CLASS) && GetNonStreamingURL(p,Value,TSIZEOF(Value),1))
- {
- tchar_t Base[MAXPATH];
- SplitURL(Value,Base,TSIZEOF(Base),Base,TSIZEOF(Base),NULL,0,NULL,0);
- if (!OpenCoverAtr(p,Base,T("cover.jpg")) &&
- !OpenCoverAtr(p,Base,T("folder.jpg")) &&
- !OpenCoverAtr(p,Base,T("front.jpg")) &&
- !OpenCoverAtr(p,Base,T("coverart.jpg")));
- }
- }
- static void FindSubtitles(format_base* p)
- {
- /*
- //!SUBTITLE
- tchar_t* SubExt[] = { T("sub"),T("str"),T("smi"), NULL };
- tchar_t** s;
- tchar_t URL[MAXPATH];
- if (!GetNonStreamingURL(p,URL,TSIZEOF(URL),0))
- return;
- for (s=SubExt;*s && !p->SubTitle;++s)
- if (SetFileExt(URL,TSIZEOF(URL),*s))
- {
- stream* Input = GetStream(URL,1);
- if (Input)
- {
- bool_t Silent = 1;
- Input->Set(Input,STREAM_SILENT,&Silent,sizeof(Silent));
- if (Input->Set(Input,STREAM_URL,URL,sizeof(tchar_t)*(tcslen(URL)+1)) == ERR_NONE)
- {
- p->SubTitle = Format_LoadSubTitle(p,Input);
- if (p->SubTitle)
- {
- UpdateTotalCount(p);
- Format_PrepairStream(p,(format_stream*)p->SubTitle);
- }
- }
- Input->Delete(Input);
- }
- }
- */
- }
- void Format_PrepairStream(format_base* p,format_stream* Stream)
- {
- switch (Stream->Format.Type)
- {
- case PACKET_VIDEO: Stream->PacketBurst = VIDEO_BURST; break;
- case PACKET_AUDIO: Stream->PacketBurst = AUDIO_BURST; break;
- case PACKET_SUBTITLE: Stream->PacketBurst = 1; break;
- }
- if (p->ProcessMinBuffer)
- p->ProcessMinBuffer = IsVideo(p) ? PROCESSMINVIDEO:PROCESSMINAUDIO;
- if (Stream->Format.Type == PACKET_AUDIO && Stream->Format.Format.Audio.Format == AUDIOFMT_PCM)
- {
- // there could be alignement problems (with memory and with audio.BlockSize)
- Stream->Fragmented = 0;
- Stream->ForceMerge = 1;
- }
- if (p->UpdateStreams.Func)
- p->UpdateStreams.Func(p->UpdateStreams.This,0,0);
- if (Stream->Format.Type == PACKET_VIDEO && p->NeedSubtitles)
- {
- p->NeedSubtitles = 0;
- FindSubtitles(p);
- }
- }
- static NOINLINE void DefaultPalette(packetformat* p)
- {
- if (p->Format.Video.Pixel.BitCount >= 1 &&
- p->Format.Video.Pixel.BitCount <= 8 &&
- p->ExtraLength >= (4 << p->Format.Video.Pixel.BitCount))
- p->Format.Video.Pixel.Palette = p->Extra;
- }
- int Format_BitmapInfoMem(format_stream* s,void* Data,int Length)
- {
- if (Length>=4)
- {
- char* p = (char*)Data;
- int Size = INT32LE(*(int32_t*)p);
- if (Length >= Size)
- {
- PacketFormatClear(&s->Format);
- s->Format.Type = PACKET_VIDEO;
- s->Format.Format.Video.Width = INT32LE(*(int32_t*)(p+4));
- s->Format.Format.Video.Height = INT32LE(*(int32_t*)(p+8));
- s->Format.Format.Video.Pixel.BitCount = INT16LE(*(int16_t*)(p+14));
- s->Format.Format.Video.Pixel.FourCC = INT32LE(*(int32_t*)(p+16));
- s->Format.Format.Video.Aspect = ASPECT_ONE; //todo
- if (PacketFormatExtra(&s->Format,Size-40))
- {
- memcpy(s->Format.Extra,p+40,s->Format.ExtraLength);
- DefaultPalette(&s->Format);
- }
- PacketFormatDefault(&s->Format);
- }
- else
- Size = 4;
- Length -= Size;
- }
- return Length;
- }
- void Format_BitmapInfo(format_reader* p,format_stream* s,int Length)
- {
- if (Length>=4)
- {
- int Size = p->ReadLE32(p); //size
- if (Length >= Size)
- {
- PacketFormatClear(&s->Format);
- s->Format.Type = PACKET_VIDEO;
- s->Format.Format.Video.Width = p->ReadLE32(p);
- s->Format.Format.Video.Height = p->ReadLE32(p);
- p->ReadLE16(p); //planes
- s->Format.Format.Video.Pixel.BitCount = p->ReadLE16(p);
- s->Format.Format.Video.Pixel.FourCC = p->ReadLE32(p);
- s->Format.Format.Video.Aspect = ASPECT_ONE; //todo
- p->Skip(p,20); //SizeImage,XPelsPerMeter,YPelsPerMeter,ClrUsed,ClrImportant
- if (PacketFormatExtra(&s->Format,Size-40))
- {
- p->Read(p,s->Format.Extra,s->Format.ExtraLength);
- DefaultPalette(&s->Format);
- }
- PacketFormatDefault(&s->Format);
- }
- else
- Size = 4;
- Length -= Size;
- }
- p->Skip(p,Length);
- }
- int Format_WaveFormatMem(format_stream* s,void* Data,int Length)
- {
- if (Length >= 16)
- {
- char* p = (char*)Data;
- PacketFormatClear(&s->Format);
- s->Format.Type = PACKET_AUDIO;
- s->Format.Format.Audio.Format = INT16LE(*(int16_t*)(p+0));
- s->Format.Format.Audio.Channels =INT16LE(*(int16_t*)(p+2));
- s->Format.Format.Audio.SampleRate = INT32LE(*(int32_t*)(p+4));
- s->Format.ByteRate = INT32LE(*(int32_t*)(p+8));
- s->Format.Format.Audio.BlockAlign = INT16LE(*(int16_t*)(p+12));
- s->Format.Format.Audio.Bits = INT16LE(*(int16_t*)(p+14));
- Length -= 16;
- if (Length >= 2)
- {
- int Extra = INT16LE(*(int16_t*)(p+16));
- Length -= 2;
- if (Extra > 0 && Length >= Extra)
- {
- if (PacketFormatExtra(&s->Format,Extra))
- memcpy(s->Format.Extra,p+18,s->Format.ExtraLength);
- Length -= Extra;
- }
- }
- PacketFormatDefault(&s->Format);
- }
- return Length;
- }
- void Format_WaveFormat(format_reader* p,format_stream* s,int Length)
- {
- if (Length >= 16)
- {
- PacketFormatClear(&s->Format);
- s->Format.Type = PACKET_AUDIO;
- s->Format.Format.Audio.Format = p->ReadLE16(p);
- s->Format.Format.Audio.Channels = p->ReadLE16(p);
- s->Format.Format.Audio.SampleRate = p->ReadLE32(p);
- s->Format.ByteRate = p->ReadLE32(p);
- s->Format.Format.Audio.BlockAlign = p->ReadLE16(p);
- s->Format.Format.Audio.Bits = p->ReadLE16(p);
- Length -= 16;
- if (Length >= 2)
- {
- int Extra = p->ReadLE16(p);
- Length -= 2;
- if (Extra > 0 && Length >= Extra)
- {
- if (PacketFormatExtra(&s->Format,Extra))
- p->Read(p,s->Format.Extra,s->Format.ExtraLength);
- Length -= Extra;
- }
- }
- PacketFormatDefault(&s->Format);
- }
- p->Skip(p,Length);
- }
- void Format_RemoveStream(format_base* p)
- {
- if (p->StreamCount)
- {
- format_stream* Stream = p->Streams[--p->StreamCount];
- UpdateTotalCount(p);
- if (p->FreeStream)
- p->FreeStream(p,Stream);
- Format_FreeStream(p,Stream);
- }
- }
- format_stream* Format_AddStream(format_base* p, int Length)
- {
- format_stream* s;
- if (p->StreamCount >= MAXSTREAM-1-1) //subtitle,coverart
- return NULL;
- s = (format_stream*) malloc(Length);
- if (!s)
- return NULL;
- memset(s,0,Length);
- s->Reader = p->Reader; // default
- s->No = p->StreamCount;
- s->LastTime = TIME_SYNC;
- p->Streams[p->StreamCount++] = s;
- UpdateTotalCount(p);
- return s;
- }
- static int Create(format_base* p)
- {
- int No;
- format_reader* Reader = p->Reader;
- for (No=0;No<MAXREADER;++No,++Reader)
- {
- Reader->Format = p;
- Reader->Seek = Reader_Seek;
- Reader->Eof = Reader_Eof;
- Reader->Skip = Reader_Skip;
- Reader->Read = Reader_Read;
- Reader->Read8 = Reader_Read8;
- Reader->ReadLE16 = Reader_ReadLE16;
- Reader->ReadBE16 = Reader_ReadBE16;
- Reader->ReadLE32 = Reader_ReadLE32;
- Reader->ReadBE32 = Reader_ReadBE32;
- Reader->ReadLE64 = Reader_ReadLE64;
- Reader->ReadBE64 = Reader_ReadBE64;
- Reader->ReadAsRef = Reader_ReadAsRef;
- Reader->GetReadBuffer = Reader_GetReadBuffer;
- }
- p->Format.Enum = (nodeenum)FormatBaseEnum;
- p->Format.Get = (nodeget)FormatBaseGet;
- p->Format.Set = (nodeset)FormatBaseSet;
- p->Format.Sync = (fmtsync)Format_Sync;
- p->Format.Read = (fmtread)Format_ReadInput;
- p->Format.Process = (fmtprocess)Format_Process;
- p->FillQueue = (fmtfill)Format_FillQueue;
- p->Process = (fmtstreamprocess)Format_ProcessStream;
- p->Sended = (fmtstream)Format_Sended;
- p->FileAlign = 1;
- p->Timing = 1;
- p->MinHeaderLoad = BLOCKSIZE;
- #ifdef TARGET_PALMOS
- p->UseBufferBlock = Context()->LowMemory;
- #endif
- return ERR_NONE;
- }
- static const nodedef FormatBase =
- {
- sizeof(format_base)|CF_ABSTRACT,
- FORMATBASE_CLASS,
- FORMAT_CLASS,
- PRI_DEFAULT,
- (nodecreate)Create,
- };
- void FormatBase_Init()
- {
- NodeRegisterClass(&FormatBase);
- }
- void FormatBase_Done()
- {
- NodeUnRegisterClass(FORMATBASE_CLASS);
- }