413 lines
9.2 KiB
C
413 lines
9.2 KiB
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: nsv.c 551 2006-01-09 11:55:09Z picard $
|
||
|
*
|
||
|
* The Core Pocket Media Player
|
||
|
* Copyright (c) 2004-2005 Gabor Kovacs
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include "../common/common.h"
|
||
|
#include "nsv.h"
|
||
|
|
||
|
typedef struct nsvstream
|
||
|
{
|
||
|
format_stream Stream;
|
||
|
uint32_t FourCC;
|
||
|
filepos_t FirstPos;
|
||
|
|
||
|
} nsvstream;
|
||
|
|
||
|
typedef struct nsv_t
|
||
|
{
|
||
|
format_base Format;
|
||
|
filepos_t Head;
|
||
|
tick_t FirstTime;
|
||
|
tick_t AVSync;
|
||
|
int AVSyncFrame;
|
||
|
bool_t NeedSync;
|
||
|
uint32_t State;
|
||
|
|
||
|
uint32_t AudioFourCC;
|
||
|
uint32_t VideoFourCC;
|
||
|
int VideoWidth;
|
||
|
int VideoHeight;
|
||
|
fraction VideoRate;
|
||
|
|
||
|
int AuxCount;
|
||
|
int AuxSize;
|
||
|
int AudioSize;
|
||
|
|
||
|
int64_t FrameState;
|
||
|
int Frame;
|
||
|
bool_t FrameValid;
|
||
|
|
||
|
} nsv;
|
||
|
|
||
|
static void Invalid(nsv* p)
|
||
|
{
|
||
|
p->AVSyncFrame = -1;
|
||
|
p->State = 0;
|
||
|
p->AuxCount = 0;
|
||
|
p->AuxSize = 0;
|
||
|
p->AudioSize = 0;
|
||
|
}
|
||
|
|
||
|
static int Init(nsv* p)
|
||
|
{
|
||
|
int i;
|
||
|
format_reader* Reader = p->Format.Reader;
|
||
|
p->Head = 0;
|
||
|
Invalid(p);
|
||
|
|
||
|
for (i=0;i<4;++i)
|
||
|
p->State = (p->State << 8) | Reader->Read8(Reader);
|
||
|
|
||
|
if (p->State == FOURCCBE('N','S','V','f'))
|
||
|
{
|
||
|
uint32_t MetaSize;
|
||
|
uint32_t Duration;
|
||
|
p->Head = Reader->FilePos - 4;
|
||
|
p->Head += Reader->ReadLE32(Reader);
|
||
|
|
||
|
Reader->Skip(Reader,4);
|
||
|
Duration = Reader->ReadLE32(Reader);
|
||
|
if (Duration != (uint32_t)-1)
|
||
|
p->Format.Duration = Scale(Duration,TICKSPERSEC,1000);
|
||
|
|
||
|
MetaSize = Reader->ReadLE32(Reader);
|
||
|
Reader->Skip(Reader,8);
|
||
|
|
||
|
//parse metadata...
|
||
|
|
||
|
Reader->Seek(Reader,p->Head,SEEK_SET);
|
||
|
}
|
||
|
|
||
|
p->Frame = 0;
|
||
|
p->FrameValid = 0;
|
||
|
p->FrameState = 0;
|
||
|
p->AVSyncFrame = -1;
|
||
|
p->NeedSync = 1;
|
||
|
p->FirstTime = 1; // but don't want to return 0
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
static INLINE bool_t AVCStartSlice(int code,int data)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#define AVC_NAL_SLICE 1
|
||
|
#define AVC_NAL_IDR_SLICE 5
|
||
|
|
||
|
static void CountFrame(nsv* p, const uint8_t* Data, int Len)
|
||
|
{
|
||
|
int i;
|
||
|
int32_t code;
|
||
|
int64_t v = p->FrameState;
|
||
|
switch (p->VideoFourCC)
|
||
|
{
|
||
|
case FOURCC('H','2','6','4'):
|
||
|
for (i=0;i<Len;++i)
|
||
|
{
|
||
|
v = (v<<8) | Data[i];
|
||
|
code = (int32_t)(v>>8) & ~0xE0;
|
||
|
if ((code == 0x100+AVC_NAL_SLICE || code == 0x100+AVC_NAL_IDR_SLICE) && (v & 0x80)==0x80) // bitgolomb()=0 -> slice_pos=0
|
||
|
++p->Frame;
|
||
|
}
|
||
|
p->FrameValid = 1;
|
||
|
break;
|
||
|
}
|
||
|
p->FrameState = v;
|
||
|
}
|
||
|
|
||
|
static void ReadData(nsv* p, format_reader* Reader, format_packet* Packet, uint32_t FourCC, int Size, int Mode)
|
||
|
{
|
||
|
nsvstream* s = NULL;
|
||
|
int n;
|
||
|
|
||
|
if (FourCC == FOURCC('N','O','N','E') || !FourCC || p->NeedSync)
|
||
|
{
|
||
|
Reader->Skip(Reader,Size);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (n=0;n<p->Format.StreamCount;++n)
|
||
|
{
|
||
|
s = (nsvstream*)p->Format.Streams[n];
|
||
|
if (s->FourCC == FourCC)
|
||
|
break;
|
||
|
}
|
||
|
if (n==p->Format.StreamCount)
|
||
|
{
|
||
|
packetformat Format;
|
||
|
memset(&Format,0,sizeof(Format));
|
||
|
Format.Type = PACKET_NONE;
|
||
|
|
||
|
switch (Mode)
|
||
|
{
|
||
|
case 0:
|
||
|
Format.Type = PACKET_VIDEO;
|
||
|
Format.Format.Video.Width = p->VideoWidth;
|
||
|
Format.Format.Video.Height = p->VideoHeight;
|
||
|
Format.Format.Video.Pixel.Flags = PF_FOURCC | PF_FRAGMENTED;
|
||
|
Format.Format.Video.Pixel.FourCC = UpperFourCC(FourCC);
|
||
|
Format.PacketRate = p->VideoRate;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
switch (FourCC)
|
||
|
{
|
||
|
case FOURCC('M','P','3',' '):
|
||
|
Format.Type = PACKET_AUDIO;
|
||
|
Format.Format.Audio.Format = AUDIOFMT_MP3;
|
||
|
break;
|
||
|
|
||
|
case FOURCC('A','A','C','P'):
|
||
|
case FOURCC('A','A','C',' '):
|
||
|
Format.Type = PACKET_AUDIO;
|
||
|
Format.Format.Audio.Format = AUDIOFMT_AAC;
|
||
|
break;
|
||
|
|
||
|
case FOURCC('S','P','X',' '):
|
||
|
Format.Type = PACKET_AUDIO;
|
||
|
Format.Format.Audio.Format = AUDIOFMT_SPEEX;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (Format.Type == PACKET_NONE || (s = (nsvstream*)Format_AddStream(&p->Format,sizeof(nsvstream)))==NULL)
|
||
|
{
|
||
|
Reader->Skip(Reader,Size);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
s->FourCC = FourCC;
|
||
|
PacketFormatCopy(&s->Stream.Format,&Format);
|
||
|
s->Stream.Fragmented = 1;
|
||
|
s->Stream.DisableDrop = Format.Type = PACKET_AUDIO;
|
||
|
s->FirstPos = -1;
|
||
|
|
||
|
Format_PrepairStream(&p->Format,&s->Stream);
|
||
|
}
|
||
|
|
||
|
if (s->FirstPos<0)
|
||
|
s->FirstPos = Reader->FilePos;
|
||
|
|
||
|
if (p->FrameValid && Mode==0)
|
||
|
Packet->RefTime = p->FirstTime + Scale(p->Frame,TICKSPERSEC*p->VideoRate.Den,p->VideoRate.Num);
|
||
|
else
|
||
|
if (p->FrameValid && Mode==1 && p->AVSyncFrame>=0)
|
||
|
{
|
||
|
Packet->RefTime = p->FirstTime + p->AVSync + Scale(p->AVSyncFrame,TICKSPERSEC*p->VideoRate.Den,p->VideoRate.Num);
|
||
|
p->AVSyncFrame = -1;
|
||
|
}
|
||
|
else
|
||
|
if (Reader->FilePos <= s->FirstPos)
|
||
|
Packet->RefTime = p->FirstTime;
|
||
|
else
|
||
|
Packet->RefTime = TIME_UNKNOWN;
|
||
|
|
||
|
Packet->Data = Reader->ReadAsRef(Reader,Size);
|
||
|
Packet->Stream = &s->Stream;
|
||
|
if (s->Stream.LastTime < TIME_UNKNOWN)
|
||
|
s->Stream.LastTime = TIME_UNKNOWN;
|
||
|
|
||
|
if (Mode==0)
|
||
|
{
|
||
|
format_ref* Ref;
|
||
|
for (Ref=Packet->Data;Ref;Ref=Ref->Next)
|
||
|
CountFrame(p,Ref->Buffer->Block.Ptr + Ref->Begin, Ref->Length);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void ReadPayload(nsv* p, format_reader* Reader)
|
||
|
{
|
||
|
int AuxCount = Reader->Read8(Reader);
|
||
|
p->AuxSize = (Reader->ReadLE16(Reader) << 4) + (AuxCount >> 4);
|
||
|
p->AuxCount = AuxCount & 15;
|
||
|
p->AudioSize = Reader->ReadLE16(Reader);
|
||
|
p->State = 0;
|
||
|
if (p->AuxSize > 32768 || p->AuxSize > 0x80000 + p->AuxCount * (0x8000+6))
|
||
|
Invalid(p);
|
||
|
}
|
||
|
|
||
|
static int ReadPacket(nsv* p, format_reader* Reader, format_packet* Packet)
|
||
|
{
|
||
|
filepos_t End = Reader->FilePos + BLOCKSIZE;
|
||
|
while (Reader->FilePos<End)
|
||
|
{
|
||
|
if (p->AuxCount>0)
|
||
|
{
|
||
|
int Size = Reader->ReadLE16(Reader);
|
||
|
if (Size > 32768)
|
||
|
Invalid(p);
|
||
|
else
|
||
|
{
|
||
|
uint32_t FourCC = Reader->ReadLE32(Reader);
|
||
|
ReadData(p,Reader,Packet,FourCC,Size,2);
|
||
|
p->AuxSize -= 6-Size;
|
||
|
--p->AuxCount;
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
if (p->AuxSize>0)
|
||
|
{
|
||
|
ReadData(p,Reader,Packet,p->VideoFourCC,p->AuxSize,0);
|
||
|
p->AuxSize = 0;
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
else
|
||
|
if (p->AudioSize>0)
|
||
|
{
|
||
|
ReadData(p,Reader,Packet,p->AudioFourCC,p->AudioSize,1);
|
||
|
p->AudioSize = 0;
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
else
|
||
|
if (p->State == FOURCCBE('N','S','V','s'))
|
||
|
{
|
||
|
int FrameRate;
|
||
|
p->VideoFourCC = Reader->ReadLE32(Reader);
|
||
|
p->AudioFourCC = Reader->ReadLE32(Reader);
|
||
|
p->VideoWidth = Reader->ReadLE16(Reader);
|
||
|
p->VideoHeight = Reader->ReadLE16(Reader);
|
||
|
|
||
|
FrameRate = Reader->Read8(Reader);
|
||
|
p->VideoRate.Num = FrameRate;
|
||
|
p->VideoRate.Den = 1;
|
||
|
if (FrameRate & 128)
|
||
|
{
|
||
|
int t = (FrameRate & 127) >> 2;
|
||
|
if (t<16)
|
||
|
{
|
||
|
p->VideoRate.Num = 1;
|
||
|
p->VideoRate.Den = t+1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p->VideoRate.Num = t-15;
|
||
|
p->VideoRate.Den = 1;
|
||
|
}
|
||
|
|
||
|
switch (FrameRate & 3)
|
||
|
{
|
||
|
case 0:
|
||
|
p->VideoRate.Num *= 30;
|
||
|
break;
|
||
|
case 1:
|
||
|
p->VideoRate.Num *= 30*1000;
|
||
|
p->VideoRate.Den *= 1001;
|
||
|
break;
|
||
|
case 2:
|
||
|
p->VideoRate.Num *= 25;
|
||
|
break;
|
||
|
case 3:
|
||
|
p->VideoRate.Num *= 24*1000;
|
||
|
p->VideoRate.Den *= 1001;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
p->AVSync = Scale((int16_t)Reader->ReadLE16(Reader),TICKSPERSEC,1000);
|
||
|
p->AVSyncFrame = p->Frame;
|
||
|
|
||
|
ReadPayload(p,Reader);
|
||
|
if (p->NeedSync && (p->AuxSize || p->AudioSize))
|
||
|
p->NeedSync = 0;
|
||
|
}
|
||
|
else
|
||
|
if ((p->State & 0xFFFF) == 0xEFBE)
|
||
|
ReadPayload(p,Reader);
|
||
|
else
|
||
|
{
|
||
|
int ch = Reader->Read8(Reader);
|
||
|
if (ch<0)
|
||
|
{
|
||
|
if (Reader->Eof(Reader))
|
||
|
return ERR_END_OF_FILE;
|
||
|
break;
|
||
|
}
|
||
|
p->State = (p->State << 8) + ch;
|
||
|
}
|
||
|
}
|
||
|
return ERR_DATA_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
static int Seek(nsv* p, tick_t Time, filepos_t FilePos,bool_t PrevKey)
|
||
|
{
|
||
|
int i;
|
||
|
if (FilePos < 0)
|
||
|
{
|
||
|
if (Time>0)
|
||
|
{
|
||
|
if (p->Format.Duration>=0 && p->Format.FileSize>=p->Head)
|
||
|
FilePos = p->Head + Scale(Time,p->Format.FileSize-p->Head,p->Format.Duration);
|
||
|
else
|
||
|
return ERR_NOT_SUPPORTED;
|
||
|
}
|
||
|
else
|
||
|
FilePos = p->Head;
|
||
|
}
|
||
|
|
||
|
Invalid(p);
|
||
|
p->NeedSync = 1;
|
||
|
p->Frame = 0;
|
||
|
p->FrameValid = 0;
|
||
|
p->FrameState = 0;
|
||
|
p->FirstTime = Time>0?Time:TIME_UNKNOWN;
|
||
|
for (i=0;i<p->Format.StreamCount;++i)
|
||
|
((nsvstream*)p->Format.Streams[i])->FirstPos = -1;
|
||
|
|
||
|
return Format_Seek(&p->Format,FilePos,SEEK_SET);
|
||
|
}
|
||
|
|
||
|
static int Create(nsv* p)
|
||
|
{
|
||
|
p->Format.Init = (fmtfunc)Init;
|
||
|
p->Format.Seek = (fmtseek)Seek;
|
||
|
p->Format.ReadPacket = (fmtreadpacket)ReadPacket;
|
||
|
p->Format.MinHeaderLoad = MINBUFFER/2;
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
static const nodedef NSV =
|
||
|
{
|
||
|
sizeof(nsv),
|
||
|
NSV_ID,
|
||
|
FORMATBASE_CLASS,
|
||
|
PRI_DEFAULT,
|
||
|
(nodecreate)Create,
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
void NSV_Init()
|
||
|
{
|
||
|
NodeRegisterClass(&NSV);
|
||
|
}
|
||
|
|
||
|
void NSV_Done()
|
||
|
{
|
||
|
NodeUnRegisterClass(NSV_ID);
|
||
|
}
|