425 lines
11 KiB
C
Executable File
425 lines
11 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: flow.c 543 2006-01-07 22:06:24Z picard $
|
|
*
|
|
* The Core Pocket Media Player
|
|
* Copyright (c) 2004-2005 Gabor Kovacs
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include "common.h"
|
|
|
|
static const datatable FlowParams[] =
|
|
{
|
|
{ FLOW_BUFFERED,TYPE_BOOL, DF_HIDDEN },
|
|
|
|
DATATABLE_END(FLOW_CLASS)
|
|
};
|
|
|
|
int FlowEnum(void* p, int* No, datadef* Param)
|
|
{
|
|
return NodeEnumTable(No,Param,FlowParams);
|
|
}
|
|
|
|
static const nodedef Flow =
|
|
{
|
|
CF_ABSTRACT,
|
|
FLOW_CLASS,
|
|
NODE_CLASS,
|
|
PRI_DEFAULT,
|
|
};
|
|
|
|
static const datatable OutParams[] =
|
|
{
|
|
{ OUT_INPUT, TYPE_PACKET, DF_INPUT },
|
|
{ OUT_OUTPUT, TYPE_PACKET, DF_OUTPUT|DF_RDONLY },
|
|
{ OUT_TOTAL, TYPE_INT, DF_HIDDEN },
|
|
{ OUT_DROPPED, TYPE_INT, DF_HIDDEN },
|
|
{ OUT_KEEPALIVE,TYPE_BOOL, DF_HIDDEN },
|
|
|
|
DATATABLE_END(OUT_CLASS)
|
|
};
|
|
|
|
int OutEnum(void* p, int* No, datadef* Param)
|
|
{
|
|
return NodeEnumTable(No,Param,OutParams);
|
|
}
|
|
|
|
static const nodedef Out =
|
|
{
|
|
CF_ABSTRACT,
|
|
OUT_CLASS,
|
|
FLOW_CLASS,
|
|
PRI_DEFAULT,
|
|
};
|
|
|
|
void Flow_Init()
|
|
{
|
|
NodeRegisterClass(&Flow);
|
|
NodeRegisterClass(&Out);
|
|
}
|
|
|
|
void Flow_Done()
|
|
{
|
|
NodeUnRegisterClass(OUT_CLASS);
|
|
NodeUnRegisterClass(FLOW_CLASS);
|
|
}
|
|
|
|
void Disconnect(node* Src,int SrcNo,node* Dst,int DstNo)
|
|
{
|
|
ConnectionUpdate(NULL,0,Dst,DstNo);
|
|
ConnectionUpdate(Src,SrcNo,NULL,0);
|
|
}
|
|
|
|
int ConnectionUpdate(node* Src,int SrcNo,node* Dst,int DstNo)
|
|
{
|
|
int Result;
|
|
pin Pin;
|
|
packetformat Format;
|
|
packetprocess Process;
|
|
|
|
memset(&Format,0,sizeof(Format));
|
|
|
|
// first setup connection via Pin, so when PIN_FORMAT is set the nodes can negotiate
|
|
// ConnectionUpdate should not disconnect nodes even is something fails
|
|
|
|
if (Dst && Dst->Get(Dst,DstNo,&Pin,sizeof(Pin))==ERR_NONE && (Pin.Node != Src || Pin.No != SrcNo))
|
|
{
|
|
Pin.Node = Src;
|
|
Pin.No = SrcNo;
|
|
Result = Dst->Set(Dst,DstNo,&Pin,sizeof(Pin));
|
|
if (Result != ERR_NONE)
|
|
return Result;
|
|
}
|
|
|
|
if (Src && Src->Get(Src,SrcNo,&Pin,sizeof(Pin))==ERR_NONE && (Pin.Node != Dst || Pin.No != DstNo))
|
|
{
|
|
Pin.Node = Dst;
|
|
Pin.No = DstNo;
|
|
Result = Src->Set(Src,SrcNo,&Pin,sizeof(Pin));
|
|
if (Result != ERR_NONE)
|
|
return Result;
|
|
}
|
|
|
|
if (Src && Dst)
|
|
{
|
|
Src->Get(Src,SrcNo|PIN_FORMAT,&Format,sizeof(Format));
|
|
Result = Dst->Set(Dst,DstNo|PIN_FORMAT,&Format,sizeof(Format));
|
|
if (Result != ERR_NONE)
|
|
Dst->Set(Dst,DstNo|PIN_FORMAT,NULL,0);
|
|
|
|
Dst->Get(Dst,DstNo|PIN_PROCESS,&Process,sizeof(packetprocess));
|
|
Src->Set(Src,SrcNo|PIN_PROCESS,&Process,sizeof(packetprocess));
|
|
|
|
if (Result != ERR_NONE)
|
|
return Result;
|
|
}
|
|
else
|
|
if (Dst)
|
|
Dst->Set(Dst,DstNo|PIN_FORMAT,NULL,0);
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
int DummyProcess(void* p, const packet* Packet, const flowstate* State)
|
|
{
|
|
if (State->CurrTime >= 0 && Packet && Packet->RefTime > State->CurrTime + SHOWAHEAD)
|
|
return ERR_BUFFER_FULL;
|
|
return ERR_NONE;
|
|
}
|
|
|
|
static bool_t ContentType(const packetformat* Format,tchar_t* Out,int OutLen)
|
|
{
|
|
tchar_t Id[16];
|
|
switch (Format->Type)
|
|
{
|
|
case PACKET_VIDEO:
|
|
FourCCToString(Id,TSIZEOF(Id),Format->Format.Video.Pixel.FourCC);
|
|
stprintf_s(Out,OutLen,T("vcodec/%s"),Id);
|
|
break;
|
|
case PACKET_AUDIO:
|
|
stprintf_s(Out,OutLen,T("acodec/0x%04x"),Format->Format.Audio.Format);
|
|
break;
|
|
case PACKET_SUBTITLE:
|
|
FourCCToString(Id,TSIZEOF(Id),Format->Format.Subtitle.FourCC);
|
|
stprintf_s(Out,OutLen,T("subtitle/%s"),Id);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void PacketFormatEnumClass(array* List, const packetformat* Format)
|
|
{
|
|
tchar_t s[16];
|
|
if (ContentType(Format,s,TSIZEOF(s)))
|
|
NodeEnumClassEx(List,FLOW_CLASS,s,NULL,NULL,0);
|
|
else
|
|
memset(List,0,sizeof(array));
|
|
}
|
|
|
|
bool_t PacketFormatMatch(int Class, const packetformat* Format)
|
|
{
|
|
tchar_t s[16];
|
|
const tchar_t* Supported = LangStr(Class,NODE_CONTENTTYPE);
|
|
assert(Supported[0]!=0);
|
|
if (Supported[0]==0)
|
|
return 0;
|
|
if (!ContentType(Format,s,TSIZEOF(s)))
|
|
return 0;
|
|
return CheckContentType(s,Supported);
|
|
}
|
|
|
|
bool_t PacketFormatSimilarAudio(const packetformat* Current, const packetformat* New)
|
|
{
|
|
return Current && Current->Type == PACKET_AUDIO && New && New->Type == PACKET_AUDIO &&
|
|
Current->Format.Audio.Format == New->Format.Audio.Format &&
|
|
Current->Format.Audio.Channels == New->Format.Audio.Channels &&
|
|
Current->Format.Audio.SampleRate == New->Format.Audio.SampleRate;
|
|
}
|
|
|
|
bool_t PacketFormatRotatedVideo(const packetformat* Current, const packetformat* New,int Mask)
|
|
{
|
|
if (Current && Current->Type == PACKET_VIDEO && New && New->Type == PACKET_VIDEO &&
|
|
(Current->Format.Video.Direction ^ New->Format.Video.Direction) & Mask)
|
|
{
|
|
video Tmp = New->Format.Video;
|
|
Tmp.Pitch = Current->Format.Video.Pitch;
|
|
if ((Current->Format.Video.Direction ^ Tmp.Direction) & DIR_SWAPXY)
|
|
SwapInt(&Tmp.Width,&Tmp.Height);
|
|
Tmp.Direction = Current->Format.Video.Direction;
|
|
return EqVideo(&Current->Format.Video,&Tmp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool_t EqPacketFormat(const packetformat* a, const packetformat* b)
|
|
{
|
|
if (!a || !b || a->Type != b->Type)
|
|
return 0;
|
|
|
|
switch (a->Type)
|
|
{
|
|
case PACKET_VIDEO: return EqVideo(&a->Format.Video,&b->Format.Video);
|
|
case PACKET_AUDIO: return EqAudio(&a->Format.Audio,&b->Format.Audio);
|
|
case PACKET_SUBTITLE: return EqSubtitle(&a->Format.Subtitle,&b->Format.Subtitle);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int PacketFormatCopy(packetformat* Dst, const packetformat* Src)
|
|
{
|
|
PacketFormatClear(Dst);
|
|
if (Src)
|
|
{
|
|
*Dst = *Src;
|
|
if (Src->ExtraLength >= 0)
|
|
{
|
|
Dst->Extra = NULL;
|
|
Dst->ExtraLength = 0;
|
|
if (Src->ExtraLength && PacketFormatExtra(Dst,Src->ExtraLength))
|
|
memcpy(Dst->Extra,Src->Extra,Dst->ExtraLength);
|
|
if (Src->Type==PACKET_VIDEO && Src->Format.Video.Pixel.Palette == (rgb*)Src->Extra)
|
|
Dst->Format.Video.Pixel.Palette = (rgb*)Dst->Extra;
|
|
}
|
|
}
|
|
return ERR_NONE;
|
|
}
|
|
|
|
void PacketFormatCombine(packetformat* Dst, const packetformat* Src)
|
|
{
|
|
if (Dst->Type == Src->Type)
|
|
{
|
|
if (!Dst->ByteRate)
|
|
Dst->ByteRate = Src->ByteRate;
|
|
if (!Dst->PacketRate.Num)
|
|
Dst->PacketRate = Src->PacketRate;
|
|
|
|
switch (Dst->Type)
|
|
{
|
|
case PACKET_VIDEO:
|
|
if (!Dst->Format.Video.Width && !Dst->Format.Video.Height)
|
|
{
|
|
Dst->Format.Video.Width = Src->Format.Video.Width;
|
|
Dst->Format.Video.Height = Src->Format.Video.Height;
|
|
Dst->Format.Video.Direction = Src->Format.Video.Direction;
|
|
}
|
|
if (!Dst->Format.Video.Aspect)
|
|
Dst->Format.Video.Aspect = Src->Format.Video.Aspect;
|
|
break;
|
|
|
|
case PACKET_AUDIO:
|
|
// force update
|
|
Dst->Format.Audio.Channels = Src->Format.Audio.Channels;
|
|
Dst->Format.Audio.SampleRate = Src->Format.Audio.SampleRate;
|
|
if (!Dst->Format.Audio.Bits)
|
|
{
|
|
Dst->Format.Audio.Bits = Src->Format.Audio.Bits;
|
|
Dst->Format.Audio.FracBits = Src->Format.Audio.FracBits;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PacketFormatClear(packetformat* p)
|
|
{
|
|
if (p->ExtraLength>=0)
|
|
free(p->Extra);
|
|
memset(p,0,sizeof(packetformat));
|
|
}
|
|
|
|
bool_t PacketFormatExtra(packetformat* p, int Length)
|
|
{
|
|
if (Length<=0)
|
|
{
|
|
if (p->ExtraLength>=0)
|
|
free(p->Extra);
|
|
p->Extra = NULL;
|
|
p->ExtraLength = 0;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
void* Extra = realloc(p->Extra,Length);
|
|
if (!Extra && Length)
|
|
return 0;
|
|
p->Extra = Extra;
|
|
p->ExtraLength = Length;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
void PacketFormatPCM(packetformat* p, const packetformat* In, int Bits)
|
|
{
|
|
PacketFormatClear(p);
|
|
p->Type = PACKET_AUDIO;
|
|
p->Format.Audio.Format = AUDIOFMT_PCM;
|
|
p->Format.Audio.Bits = Bits;
|
|
p->Format.Audio.SampleRate = In->Format.Audio.SampleRate;
|
|
p->Format.Audio.Channels = In->Format.Audio.Channels;
|
|
if (p->Format.Audio.Channels > 2)
|
|
p->Format.Audio.Channels = 2;
|
|
PacketFormatDefault(p);
|
|
}
|
|
|
|
void PacketFormatDefault(packetformat* p)
|
|
{
|
|
switch (p->Type)
|
|
{
|
|
case PACKET_VIDEO:
|
|
if (p->Format.Video.Pixel.FourCC==0) // DIB?
|
|
{
|
|
if (p->Format.Video.Pixel.BitCount <= 8)
|
|
p->Format.Video.Pixel.Flags = PF_PALETTE;
|
|
else
|
|
switch (p->Format.Video.Pixel.BitCount)
|
|
{
|
|
case 16:
|
|
DefaultRGB(&p->Format.Video.Pixel,p->Format.Video.Pixel.BitCount,5,5,5,0,0,0);
|
|
break;
|
|
case 24:
|
|
case 32:
|
|
DefaultRGB(&p->Format.Video.Pixel,p->Format.Video.Pixel.BitCount,8,8,8,0,0,0);
|
|
break;
|
|
}
|
|
p->Format.Video.Direction = DIR_MIRRORUPDOWN;
|
|
}
|
|
else
|
|
if (p->Format.Video.Pixel.FourCC==3)
|
|
{
|
|
p->Format.Video.Pixel.Flags = PF_RGB;
|
|
p->Format.Video.Direction = DIR_MIRRORUPDOWN;
|
|
}
|
|
else
|
|
{
|
|
p->Format.Video.Pixel.Flags = PF_FOURCC;
|
|
p->Format.Video.Pixel.FourCC = UpperFourCC(p->Format.Video.Pixel.FourCC);
|
|
p->Format.Video.Direction = 0;
|
|
}
|
|
|
|
DefaultPitch(&p->Format.Video);
|
|
|
|
if (p->Format.Video.Height<0)
|
|
{
|
|
p->Format.Video.Height = -p->Format.Video.Height;
|
|
p->Format.Video.Direction ^= DIR_MIRRORUPDOWN;
|
|
}
|
|
break;
|
|
|
|
case PACKET_AUDIO:
|
|
// detect fake PCM
|
|
if (p->Format.Audio.Format > 8192 &&
|
|
p->Format.Audio.BlockAlign > 0 &&
|
|
p->ByteRate > 0 &&
|
|
p->Format.Audio.BlockAlign == ((p->Format.Audio.Channels * p->Format.Audio.Bits) >> 3) &&
|
|
p->ByteRate == p->Format.Audio.SampleRate * p->Format.Audio.BlockAlign)
|
|
p->Format.Audio.Format = AUDIOFMT_PCM;
|
|
|
|
if (p->Format.Audio.Format == AUDIOFMT_PCM)
|
|
{
|
|
p->Format.Audio.FracBits = p->Format.Audio.Bits - 1;
|
|
if (p->Format.Audio.Bits <= 8)
|
|
p->Format.Audio.Flags |= PCM_UNSIGNED;
|
|
p->Format.Audio.Bits = ALIGN8(p->Format.Audio.Bits);
|
|
p->Format.Audio.BlockAlign = (p->Format.Audio.Channels * p->Format.Audio.Bits) >> 3;
|
|
p->ByteRate = p->Format.Audio.SampleRate * p->Format.Audio.BlockAlign;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool_t PacketFormatName(packetformat* p, tchar_t* Name, int NameLen)
|
|
{
|
|
tchar_t Id[8];
|
|
switch (p->Type)
|
|
{
|
|
case PACKET_SUBTITLE:
|
|
tcscpy_s(Name,NameLen,LangStr(p->Format.Subtitle.FourCC,0x4400));
|
|
if (!Name[0])
|
|
FourCCToString(Name,NameLen,p->Format.Subtitle.FourCC);
|
|
return 1;
|
|
case PACKET_AUDIO:
|
|
if (p->Format.Audio.Format != AUDIOFMT_PCM)
|
|
{
|
|
stprintf_s(Id,TSIZEOF(Id),T("%04X"),p->Format.Audio.Format);
|
|
tcscpy_s(Name,NameLen,LangStr(FOURCC(Id[0],Id[1],Id[2],Id[3]),0x4400));
|
|
if (!Name[0])
|
|
tcscpy_s(Name,NameLen,Id);
|
|
}
|
|
else
|
|
tcscpy_s(Name,NameLen,T("PCM"));
|
|
return 1;
|
|
case PACKET_VIDEO:
|
|
if (Compressed(&p->Format.Video.Pixel))
|
|
{
|
|
tcscpy_s(Name,NameLen,LangStr(p->Format.Video.Pixel.FourCC,0x4400));
|
|
if (!Name[0])
|
|
FourCCToString(Name,NameLen,p->Format.Video.Pixel.FourCC);
|
|
}
|
|
else
|
|
if (AnyYUV(&p->Format.Video.Pixel))
|
|
tcscpy_s(Name,NameLen,T("YUV"));
|
|
else
|
|
stprintf_s(Name,NameLen,T("RGB %d bits"),p->Format.Video.Pixel.BitCount);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|