player.c
资源名称:tcpmp.rar [点击查看]
上传用户:wstnjxml
上传日期:2014-04-03
资源大小:7248k
文件大小:109k
源码类别:
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: player.c 623 2006-02-01 13:19:15Z picard $
- *
- * The Core Pocket Media Player
- * Copyright (c) 2004-2005 Gabor Kovacs
- *
- ****************************************************************************/
- #include "common.h"
- #include "playlist/m3u.h"
- #include "../config.h"
- #define VIDEO_STREAMING_UNDERRUN (256*1024)/BLOCKSIZE
- #define ALIGN_SIZE 4096
- #define PROBE_SIZE ALIGN_SIZE
- #define MAXSKIP 16
- typedef struct ref
- {
- node* Node;
- int RefCount;
- } ref;
- typedef struct item
- {
- tchar_t URL[MAXPATH];
- tchar_t Title[256];
- tick_t Length;
- bool_t Changed;
- } item;
- typedef struct player_base
- {
- player Player;
- bool_t BufferWarning;
- int BufferSize2; //in blocksize
- int MDBufferSize2; //in blocksize
- int CurrBufferSize2; //in blocksize
- bool_t SoftVolume;
- bool_t Background;
- bool_t Foreground;
- bool_t PowerOff;
- bool_t PowerOffReSync;
- bool_t FlowBackground;
- bool_t MicroDrive;
- bool_t Repeat;
- bool_t Shuffle;
- bool_t PlayAtOpen;
- bool_t PlayAtOpenFull;
- bool_t ExitAtEnd;
- bool_t KeepPlayVideo;
- bool_t KeepPlayAudio;
- bool_t ShowInBackground;
- bool_t SingleClickFullScreen;
- bool_t KeepList;
- tick_t MoveBack;
- tick_t MoveFFwd;
- int BurstStart; // in blocksize
- int UnderRun; // percent
- int AudioUnderRun; //kbyte
- fraction FullZoom;
- fraction SkinZoom;
- fraction Aspect;
- int FullDir;
- int SkinDir;
- int RelDir;
- bool_t SmoothZoom50;
- bool_t SmoothZoomAlways;
- bool_t VideoAccel;
- bool_t AutoPreRotate;
- int SkipAccelFrom;
- int AudioQuality;
- int VideoQuality;
- int AOutputId;
- int VOutputId;
- int AOutputIdMax;
- int VOutputIdMax;
- fraction SeekAfterSync;
- bool_t SeekAfterSyncPrevKey;
- fraction InSeekPos;
- fraction PlaySpeed;
- fraction FFwdSpeed;
- int Volume;
- int Pan;
- int PreAmp;
- bool_t Mute;
- bool_t TimerLeft;
- node* Color;
- node* Equalizer;
- bool_t Eq;
- // states
- notify ListNotify;
- notify Notify;
- rect SkinViewport;
- void* Wnd;
- bool_t DiscardList;
- bool_t FullScreen;
- bool_t Clipping;
- bool_t Rotating;
- rect Viewport;
- bool_t Primary;
- bool_t Overlay;
- bool_t HasHost;
- bool_t Streaming; // HasHost and filesize==-1
- bool_t Timing;
- bool_t TemporaryHidden; // hidden till next sync
- bool_t FullScreenAfterSync;
- int Stereo;
- int StreamNo;
- // current media
- bool_t PreRotate;
- int BufferMax;
- stream* Input;
- stream* InputBase;
- node* Timer;
- node* AOutput;
- node* VOutput;
- node* VIDCT;
- node* FlowBuffer;
- format* Format;
- int Selected[PACKET_MAX]; // stream numbers
- // threads and timings
- bool_t NoMoreInput;
- bool_t Play;
- bool_t FFwd;
- bool_t Sync;
- bool_t Fill;
- bool_t LoadMode;
- bool_t Bench;
- bool_t SaveMicroDrive;
- bool_t WaitForProcess;
- bool_t TimerPlay;
- bool_t InSeek;
- bool_t UpdateStreamsNeeded;
- tick_t Position;
- tick_t PosNotify;
- fraction Speed;
- tick_t PosNotifyStep;
- tick_t BenchTime;
- point BenchSrc;
- point BenchDst;
- int BenchStart;
- int IOError;
- int UsedEnough2; //in blocksize
- int MinBuffer; //in blocksize
- uint32_t RndSeed;
- bool_t RunInput;
- bool_t RunProcess;
- #ifdef MULTITHREAD
- int ProcessPriority;
- int InputPriority;
- int LockInputCount; // just hint to sleep in input thread so other other thread can lock it
- int LockProcessCount; // just hint to sleep in process thread so other other thread can lock it
- void* Lock; // parameter read/write
- void* LockComment;
- void* LockInput;
- void* LockProcess;
- void* EventProcess; // ProcessThread has processed some packets (InputThread can continue)
- void* EventRunInput;
- void* EventRunProcess;
- void* ProcessThread;
- void* InputThread;
- int ProcessLocked;
- int InputLocked;
- #endif
- // playlist
- int Current;
- bool_t CurrentChanged;
- int PlayListCount;
- item* PlayList;
- int* PlayIndex;
- // paint
- rgbval_t ColorKey; // colorkeybursh color
- void* ColorKeyBrush;
- void* BlackBrush;
- array Ref; // ref
- array Comment; // [Stream,Name,0,Value,0]
- bool_t StreamSkip[MAXSTREAM];
- tchar_t Title[256];
- tchar_t CurrentDir[MAXPATH];
- int CodecSkipCount;
- int CodecSkip[MAXSKIP];
- } player_base;
- static const datatable PlayerParams[] =
- {
- { PLAYER_AUTOPREROTATE, TYPE_BOOL, DF_SETUP },
- { PLAYER_REPEAT, TYPE_BOOL, DF_SETUP|DF_HIDDEN },
- { PLAYER_SHUFFLE, TYPE_BOOL, DF_SETUP|DF_HIDDEN },
- { PLAYER_KEEPPLAY_AUDIO,TYPE_BOOL, DF_SETUP },
- { PLAYER_KEEPPLAY_VIDEO,TYPE_BOOL, DF_SETUP },
- { PLAYER_SHOWINBACKGROUND,TYPE_BOOL, DF_SETUP },
- { PLAYER_SINGLECLICKFULLSCREEN, TYPE_BOOL, DF_SETUP },
- { PLAYER_KEEPLIST, TYPE_BOOL, DF_SETUP },
- { PLAYER_PLAYATOPEN, TYPE_BOOL, DF_SETUP },
- { PLAYER_PLAYATOPEN_FULL,TYPE_BOOL, DF_SETUP },
- { PLAYER_EXIT_AT_END, TYPE_BOOL, DF_SETUP },
- { PLAYER_FULL_ZOOM, TYPE_FRACTION, DF_SETUP|DF_PERCENT|DF_HIDDEN },
- { PLAYER_SKIN_ZOOM, TYPE_FRACTION, DF_SETUP|DF_PERCENT|DF_HIDDEN },
- { PLAYER_PLAY_SPEED, TYPE_FRACTION, DF_SETUP|DF_PERCENT|DF_GAP },
- { PLAYER_FFWD_SPEED, TYPE_FRACTION, DF_SETUP|DF_PERCENT },
- { PLAYER_MOVEFFWD_STEP, TYPE_TICK, DF_SETUP|DF_MINMAX, 0, 600*TICKSPERSEC },
- { PLAYER_MOVEBACK_STEP, TYPE_TICK, DF_SETUP|DF_MINMAX, 0, 600*TICKSPERSEC },
- { PLAYER_BUFFER_SIZE, TYPE_INT, DF_SETUP|DF_KBYTE|DF_GAP|DF_HIDDEN, 512, 128*1024 },
- { PLAYER_UNDERRUN, TYPE_INT, DF_SETUP|DF_PERCENT|DF_HIDDEN,0,PERCENT_ONE },
- { PLAYER_AUDIO_UNDERRUN,TYPE_INT, DF_SETUP|DF_KBYTE|DF_HIDDEN,32,128*1024 },
- { PLAYER_MICRODRIVE, TYPE_BOOL, DF_SETUP|DF_GAP|DF_HIDDEN },
- { PLAYER_MD_BUFFER_SIZE,TYPE_INT, DF_SETUP|DF_KBYTE|DF_HIDDEN, 512, 128*1024 },
- { PLAYER_BURSTSTART, TYPE_INT, DF_SETUP|DF_KBYTE|DF_HIDDEN, 512, 128*1024 },
- { PLAYER_AOUTPUTID, TYPE_INT, DF_SETUP|DF_HIDDEN },
- { PLAYER_AOUTPUTID_MAX, TYPE_INT, DF_SETUP|DF_HIDDEN }, // should be after PLAYER_AOUTPUTID
- { PLAYER_VOUTPUTID, TYPE_INT, DF_SETUP|DF_HIDDEN },
- { PLAYER_VOUTPUTID_MAX, TYPE_INT, DF_SETUP|DF_HIDDEN }, // should be after PLAYER_VOUTPUTID
- { PLAYER_VIDEO_ACCEL, TYPE_BOOL, DF_SETUP|DF_HIDDEN },
- { PLAYER_LIST_COUNT, TYPE_INT, DF_SETUP|DF_HIDDEN },
- { PLAYER_LIST_CURRENT, TYPE_INT, DF_SETUP|DF_HIDDEN },
- { PLAYER_LIST_CURRIDX, TYPE_INT, DF_HIDDEN },
- { PLAYER_SMOOTH50, TYPE_BOOL, DF_SETUP|DF_HIDDEN },
- { PLAYER_SMOOTHALWAYS, TYPE_BOOL, DF_SETUP|DF_HIDDEN },
- { PLAYER_STEREO, TYPE_INT, DF_SETUP|DF_HIDDEN },
- { PLAYER_ASPECT, TYPE_FRACTION, DF_SETUP|DF_HIDDEN },
- { PLAYER_FULL_DIR, TYPE_INT, DF_SETUP|DF_HIDDEN },
- { PLAYER_SKIN_DIR, TYPE_INT, DF_SETUP|DF_HIDDEN },
- { PLAYER_AUDIO_QUALITY, TYPE_INT, DF_SETUP|DF_ENUMSTRING|DF_HIDDEN, PLAYERQUALITY_ENUM },
- { PLAYER_VIDEO_QUALITY, TYPE_INT, DF_SETUP|DF_ENUMSTRING|DF_HIDDEN, PLAYERQUALITY_ENUM },
- { PLAYER_ASTREAM, TYPE_INT, DF_HIDDEN|DF_SETUP },
- { PLAYER_VSTREAM, TYPE_INT, DF_HIDDEN|DF_SETUP },
- { PLAYER_SUBSTREAM, TYPE_INT, DF_HIDDEN|DF_SETUP },
- { PLAYER_PERCENT, TYPE_FRACTION, DF_HIDDEN|DF_SETUP },
- { PLAYER_VOLUME, TYPE_INT, DF_MINMAX|DF_HIDDEN|DF_SETUP, 0, 100 },
- { PLAYER_PAN, TYPE_INT, DF_MINMAX|DF_HIDDEN|DF_SETUP, -100, 100 },
- { PLAYER_PREAMP, TYPE_INT, DF_MINMAX|DF_HIDDEN|DF_SETUP, -100, 100 },
- { PLAYER_MUTE, TYPE_BOOL, DF_HIDDEN|DF_SETUP },
- { PLAYER_TIMER_LEFT, TYPE_BOOL, DF_HIDDEN|DF_SETUP },
- { PLAYER_BACKGROUND, TYPE_BOOL, DF_HIDDEN },
- { PLAYER_FOREGROUND, TYPE_BOOL, DF_HIDDEN },
- { PLAYER_PLAY, TYPE_BOOL, DF_HIDDEN },
- { PLAYER_FFWD, TYPE_BOOL, DF_HIDDEN },
- { PLAYER_POSITION, TYPE_TICK, DF_HIDDEN },
- { PLAYER_DURATION, TYPE_TICK, DF_HIDDEN },
- { PLAYER_TIMER, TYPE_STRING, DF_HIDDEN },
- { PLAYER_TITLE, TYPE_STRING },
- { PLAYER_INPUT, TYPE_NODE, DF_HIDDEN, STREAM_CLASS },
- { PLAYER_FORMAT, TYPE_NODE, DF_HIDDEN, FORMAT_CLASS },
- { PLAYER_AOUTPUT, TYPE_NODE, DF_HIDDEN, AOUT_CLASS },
- { PLAYER_VOUTPUT, TYPE_NODE, DF_HIDDEN, VOUT_CLASS },
- { PLAYER_SKIN_VIEWPORT, TYPE_RECT, DF_HIDDEN },
- { PLAYER_CLIPPING, TYPE_BOOL, DF_HIDDEN },
- { PLAYER_FULLSCREEN, TYPE_BOOL, DF_HIDDEN },
- { PLAYER_LOADMODE, TYPE_BOOL, DF_HIDDEN | DF_RDONLY },
- { PLAYER_CURRENTDIR, TYPE_STRING, DF_SETUP|DF_HIDDEN },
- DATATABLE_END(PLAYER_ID)
- };
- static const datatable BufferParams[] =
- {
- { PLAYER_BUFFER_SIZE, TYPE_INT, DF_SETUP|DF_KBYTE|DF_GAP, 512, 128*1024 },
- { PLAYER_UNDERRUN, TYPE_INT, DF_SETUP|DF_PERCENT,0,PERCENT_ONE },
- { PLAYER_AUDIO_UNDERRUN,TYPE_INT, DF_SETUP|DF_KBYTE,32,128*1024 },
- { PLAYER_MICRODRIVE, TYPE_BOOL, DF_SETUP|DF_GAP },
- { PLAYER_MD_BUFFER_SIZE,TYPE_INT, DF_SETUP|DF_KBYTE, 512, 128*1024 },
- { PLAYER_BURSTSTART, TYPE_INT, DF_SETUP|DF_KBYTE, 512, 128*1024 },
- DATATABLE_END(PLAYER_BUFFER_ID)
- };
- static int Enum(player_base* p, int* No, datadef* Param)
- {
- if (NodeEnumTable(No,Param,PlayerParams) == ERR_NONE)
- return ERR_NONE;
- if (*No<p->PlayListCount*3)
- {
- memset(Param,0,sizeof(datadef));
- if (*No >= 2*p->PlayListCount)
- {
- Param->No = PLAYER_LIST_LENGTH + (*No - 2*p->PlayListCount);
- Param->Type = TYPE_TICK;
- Param->Size = sizeof(tick_t);
- }
- else
- {
- if (*No >= p->PlayListCount)
- Param->No = PLAYER_LIST_TITLE + (*No - p->PlayListCount);
- else
- Param->No = PLAYER_LIST_URL + *No;
- Param->Type = TYPE_STRING;
- Param->Size = MAXDATA;
- }
- Param->Flags = DF_HIDDEN | DF_SETUP;
- Param->Class = PLAYER_ID;
- return ERR_NONE;
- }
- *No -= p->PlayListCount*3;
- return ERR_INVALID_PARAM;
- }
- static NOINLINE int UpdateSpeed(player_base* p)
- {
- if (p->Bench)
- {
- p->Speed.Num = 0;
- p->Speed.Den = 1;
- }
- else
- if (p->FFwd)
- p->Speed = p->FFwdSpeed;
- else
- p->Speed = p->PlaySpeed;
- p->PosNotifyStep = Scale(TICKSPERSEC,p->Speed.Num,p->Speed.Den);
- if (p->Timer)
- p->Timer->Set(p->Timer,TIMER_SPEED,&p->Speed,sizeof(p->Speed));
- return ERR_NONE;
- }
- static NOINLINE void NotifyList(player_base* p)
- {
- if (p->ListNotify.Func)
- p->ListNotify.Func(p->ListNotify.This,0,0);
- }
- static NOINLINE void Notify(player_base* p,int Id,int Value)
- {
- if (p->Notify.Func)
- p->Notify.Func(p->Notify.This,Id,Value);
- }
- static NOINLINE void UpdateListLength(player_base* p,tick_t Length)
- {
- if (p->Current < p->PlayListCount && !p->PlayList[p->Current].Changed &&
- p->PlayList[p->Current].Length != Length)
- {
- p->PlayList[p->Current].Length = Length;
- NotifyList(p);
- }
- }
- #define INVALID_COMMENT -2
- static INLINE int CommentStream(const uint8_t* p) { return *(tchar_t*)p; }
- static INLINE const tchar_t* CommentName(const uint8_t* p) { return (const tchar_t*)(p+sizeof(tchar_t)); }
- static INLINE const tchar_t* CommentValue(const uint8_t* p) { const tchar_t* i = CommentName(p); return i+tcslen(i)+1; }
- static const uint8_t* CommentNext(const uint8_t* p) { const tchar_t* i = CommentValue(p); return (const uint8_t*)(i+tcslen(i)+1); }
- static const tchar_t* GetFirstComment(player_base* p, int Id)
- {
- const uint8_t *i;
- const tchar_t* Name = PlayerComment(Id);
- for (i=ARRAYBEGIN(p->Comment,uint8_t);i!=ARRAYEND(p->Comment,uint8_t);i=CommentNext(i))
- if (CommentStream(i)!=INVALID_COMMENT && tcsicmp(CommentName(i),Name)==0)
- return CommentValue(i);
- return T("");
- }
- static void UpdateListTitle(player_base* p)
- {
- const tchar_t* Title;
- const tchar_t* Artist;
- LockEnter(p->LockComment);
- Title = GetFirstComment(p,COMMENT_TITLE);
- Artist = GetFirstComment(p,COMMENT_ARTIST);
- if (!Artist[0])
- Artist = GetFirstComment(p,COMMENT_AUTHOR);
- if (Artist[0] && Title[0])
- stprintf_s(p->Title,TSIZEOF(p->Title),T("%s - %s"),Artist,Title);
- else
- {
- if (!Title[0])
- Title = Artist;
- tcscpy_s(p->Title,TSIZEOF(p->Title),Title);
- }
- LockLeave(p->LockComment);
- Notify(p,PLAYER_TITLE,0);
- if (p->Current < p->PlayListCount && !p->PlayList[p->Current].Changed)
- {
- tcscpy_s(p->PlayList[p->Current].Title,TSIZEOF(p->PlayList[p->Current].Title),p->Title);
- NotifyList(p);
- }
- }
- static NOINLINE bool_t IsCoverArt(player_base* p)
- {
- int CoverArt = -2;
- if (p->Format)
- p->Format->Get(p->Format,FORMAT_COVERART,&CoverArt,sizeof(CoverArt));
- return CoverArt==p->Selected[PACKET_VIDEO];
- }
- static NOINLINE bool_t IsVideo(player_base* p)
- {
- return p->VOutput && !IsCoverArt(p);
- }
- static NOINLINE void UpdateTimeouts(player_base* p)
- {
- bool_t KeepProcess = (p->Play || p->FFwd) && p->Format;
- if (!KeepProcess || !p->Sync) // VOutput not neccessary updated till first sync is over
- SleepTimeout(KeepProcess,p->Bench || (p->Primary && IsVideo(p)));
- }
- static NOINLINE void UpdateRunInput(player_base* p)
- {
- // we don't want deadlocks so LoadMode will restart InputThread even with NoMoreInput
- p->RunInput = p->Format && p->Input && (!p->NoMoreInput || p->LoadMode);
- #ifdef MULTITHREAD
- if (p->RunInput)
- EventSet(p->EventRunInput);
- else
- EventReset(p->EventRunInput);
- #endif
- }
- static NOINLINE void UpdateRunProcess(player_base* p)
- {
- p->RunProcess = p->Format && p->Input && p->Timer && (((p->FFwd || p->Play) && !p->LoadMode) || p->Sync || p->Fill);
- #ifdef MULTITHREAD
- if (p->RunProcess)
- EventSet(p->EventRunProcess);
- else
- EventReset(p->EventRunProcess);
- #endif
- }
- static NOINLINE void UpdatePriority(player_base* p)
- {
- #ifdef MULTITHREAD
- if (p->InputThread && p->ProcessThread)
- {
- int Priority;
- Priority = (!p->TimerPlay || !QueryAdvanced(ADVANCED_PRIORITY))?0:1;
- if (p->ProcessPriority != Priority)
- {
- p->ProcessPriority = Priority;
- ThreadPriority(p->ProcessThread,Priority);
- }
- Priority = (((p->Play || p->FFwd) && !p->Sync) || p->LoadMode)?0:1;
- if (p->InputPriority != Priority)
- {
- p->InputPriority = Priority;
- ThreadPriority(p->InputThread,Priority);
- }
- }
- #endif
- }
- static NOINLINE void UpdatePlay(player_base* p)
- {
- p->TimerPlay = (p->Play || p->FFwd) && !p->InSeek && !p->Sync && !p->Fill && !p->LoadMode;
- UpdatePriority(p);
- p->Timer->Set(p->Timer,TIMER_PLAY,&p->TimerPlay,sizeof(bool_t));
- if (p->VOutput)
- p->VOutput->Set(p->VOutput,VOUT_PLAY,&p->TimerPlay,sizeof(bool_t));
- }
- static int CmpRef(const ref* a, const ref* b)
- {
- if (a->Node > b->Node) return 1;
- if (a->Node < b->Node) return -1;
- return 0;
- }
- static void Unlock(player_base* p, bool_t Input);
- static void Lock(player_base* p, bool_t Input);
- static NOINLINE bool_t Used(player_base* p,node* Node)
- {
- const ref *i;
- for (i=ARRAYBEGIN(p->Ref,ref);i!=ARRAYEND(p->Ref,ref);++i)
- if (i->Node == Node)
- return 1;
- return 0;
- }
- static NOINLINE void Flush(player_base* p)
- {
- const ref *i;
- node* Skip = NULL;
- if (IsCoverArt(p))
- Skip = p->FlowBuffer;
- for (i=ARRAYBEGIN(p->Ref,ref);i!=ARRAYEND(p->Ref,ref);++i)
- if (i->Node != Skip)
- i->Node->Set(i->Node,FLOW_FLUSH,NULL,0);
- if (p->FlowBuffer && p->FlowBuffer != Skip)
- p->FlowBuffer->Set(p->FlowBuffer,FLOW_FLUSH,NULL,0);
- }
- static NOINLINE int UpdateBackground(player_base* p)
- {
- bool_t b;
- #ifdef MULTITHREAD
- assert(p->ProcessLocked != ThreadId() && p->InputLocked != ThreadId());
- #endif
- LockEnter(p->Lock);
- b = p->Background && !p->ShowInBackground && !p->Play && !p->FFwd;
- #if !defined(TARGET_WINCE)
- if (p->PowerOff) b = 1;
- #endif
- if (b != p->FlowBackground)
- {
- bool_t Locked = 0;
- const ref *i;
- p->FlowBackground = b;
- for (i=ARRAYBEGIN(p->Ref,ref);i!=ARRAYEND(p->Ref,ref);++i)
- if (i->Node->Get(i->Node,FLOW_BACKGROUND,&b,sizeof(b))==ERR_NONE)
- {
- if (!Locked)
- {
- Lock(p,1);
- Locked = 1;
- }
- i->Node->Set(i->Node,FLOW_BACKGROUND,&p->FlowBackground,sizeof(p->FlowBackground));
- }
- if (Locked)
- Unlock(p,1);
- }
- LockLeave(p->Lock);
- return ERR_NONE;
- }
- static void ReSync(player_base* p,bool_t Seek);
- static int UpdateFormat(player_base* p,bool_t BufferChanged);
- static int SetPlay(player_base* p,bool_t Stop);
- static int SetPlayListCount(player_base* p, int Count);
- static void Paint(player_base* p,void* DC,int x0,int y0);
- static int UpdateVideoEx(player_base* p,node* VOutput,int ForceRefresh,bool_t Active)
- {
- int Caps;
- bool_t Dither;
- packetformat Mode;
- blitfx FX;
- bool_t FullScreen;
- bool_t Visible = (p->Foreground || p->ShowInBackground) && !p->Rotating && Active;
- bool_t Refresh;
- bool_t Updating;
- bool_t VOutVisible;
- bool_t Primary;
- bool_t Overlay;
- rect Viewport;
- int RelDir;
- if (VOutput)
- {
- VOutput->Get(VOutput,VOUT_PRIMARY,&Primary,sizeof(bool_t));
- Viewport = p->SkinViewport;
- FullScreen = p->FullScreen || !Primary;
- if (FullScreen && VOutput->Get(VOutput,OUT_OUTPUT|PIN_FORMAT,&Mode,sizeof(Mode))==ERR_NONE && Mode.Type == PACKET_VIDEO)
- PhyToVirt(NULL,&Viewport,&Mode.Format.Video);
- FX.Flags = 0;
- if (p->SmoothZoom50)
- FX.Flags |= BLITFX_ARITHSTRETCH50;
- if (p->SmoothZoomAlways)
- FX.Flags |= BLITFX_ARITHSTRETCHALWAYS;
- if (QueryAdvanced(ADVANCED_SLOW_VIDEO))
- FX.Flags |= BLITFX_ONLYDIFF;
- if (QueryAdvanced(ADVANCED_COLOR_LOOKUP))
- FX.Flags |= BLITFX_COLOR_LOOKUP;
- p->Color->Get(p->Color,COLOR_DITHER,&Dither,sizeof(Dither));
- if (Dither)
- FX.Flags |= BLITFX_DITHER;
- p->Color->Get(p->Color,COLOR_BRIGHTNESS,&FX.Brightness,sizeof(FX.Brightness));
- p->Color->Get(p->Color,COLOR_CONTRAST,&FX.Contrast,sizeof(FX.Contrast));
- p->Color->Get(p->Color,COLOR_SATURATION,&FX.Saturation,sizeof(FX.Saturation));
- p->Color->Get(p->Color,COLOR_R_ADJUST,&FX.RGBAdjust[0],sizeof(FX.RGBAdjust[0]));
- p->Color->Get(p->Color,COLOR_G_ADJUST,&FX.RGBAdjust[1],sizeof(FX.RGBAdjust[1]));
- p->Color->Get(p->Color,COLOR_B_ADJUST,&FX.RGBAdjust[2],sizeof(FX.RGBAdjust[2]));
- if (FullScreen)
- {
- FX.Direction = p->FullDir;
- FX.ScaleY = FX.ScaleX = Scale(SCALE_ONE,p->FullZoom.Num,p->FullZoom.Den);
- }
- else
- {
- FX.Direction = p->SkinDir;
- FX.ScaleY = FX.ScaleX = Scale(SCALE_ONE,p->SkinZoom.Num,p->SkinZoom.Den);
- }
- if (FX.Direction < 0)
- FX.Direction = 0;
- else
- if (Primary)
- FX.Direction = CombineDir(GetOrientation(),0,FX.Direction);
- RelDir = FX.Direction;
- VOutVisible = Visible && !p->TemporaryHidden;
- Updating = 1;
- VOutput->Set(VOutput,VOUT_UPDATING,&Updating,sizeof(bool_t));
- VOutput->Set(VOutput,VOUT_FULLSCREEN,&FullScreen,sizeof(bool_t));
- VOutput->Set(VOutput,VOUT_VIEWPORT,&Viewport,sizeof(rect));
- VOutput->Set(VOutput,VOUT_FX,&FX,sizeof(blitfx));
- VOutput->Set(VOutput,VOUT_ASPECT,&p->Aspect,sizeof(fraction));
- VOutput->Set(VOutput,VOUT_CLIPPING,&p->Clipping,sizeof(p->Clipping));
- VOutput->Set(VOutput,VOUT_VISIBLE,&VOutVisible,sizeof(VOutVisible));
- VOutput->Set(VOutput,VOUT_AUTOPREROTATE,&p->AutoPreRotate,sizeof(bool_t));
- Updating = 0;
- VOutput->Set(VOutput,VOUT_UPDATING,&Updating,sizeof(bool_t));
- VOutput->Get(VOutput,VOUT_OVERLAY,&Overlay,sizeof(bool_t));
- }
- else
- {
- Primary = 1;
- Overlay = 0;
- RelDir = 0;
- Viewport = p->SkinViewport;
- }
- if (Active)
- {
- p->RelDir = RelDir;
- p->Primary = Primary;
- p->Overlay = Overlay;
- p->Viewport = Viewport;
- #ifdef TARGET_PALMOS
- if (!p->Clipping && Visible)
- Paint(p,p->Wnd,0,0);
- #endif
- // try to redraw overlay
- Refresh = VOutput && ForceRefresh>=0 && !p->Sync;
- if (Refresh && p->FlowBuffer && !ForceRefresh)
- Refresh = p->FlowBuffer->Set(p->FlowBuffer,FLOW_RESEND,NULL,0) != ERR_NONE;
- if (Refresh && p->Format)
- ReSync(p,0); // redraw the hard way: resync
- if (!p->Wnd || !VOutput || VOutput->Get(VOutput,VOUT_CAPS,&Caps,sizeof(Caps))!=ERR_NONE)
- Caps = -1;
- p->Color->Set(p->Color,COLOR_CAPS,&Caps,sizeof(Caps));
- }
- return ERR_NONE;
- }
- static NOINLINE int UpdateVideo(player_base* p,int ForceRefresh)
- {
- return UpdateVideoEx(p,p->VOutput,ForceRefresh,1);
- }
- static NOINLINE int UpdateBuffer(player_base* p)
- {
- node* Node = p->AOutput;
- if (Node)
- {
- bool_t b = IsVideo(p);
- Node->Set(Node,AOUT_MODE,&b,sizeof(b));
- }
- p->MinBuffer = 0;
- if (p->Format)
- p->Format->Get(p->Format,FORMAT_MIN_BUFFER,&p->MinBuffer,sizeof(int));
- return ERR_NONE;
- }
- static NOINLINE player_base* RefreshAudio(player_base* p)
- {
- node* Node = p->AOutput;
- if (Node && QueryAdvanced(ADVANCED_SYSTEMVOLUME))
- {
- Node->Get(Node,AOUT_VOLUME,&p->Volume,sizeof(int));
- Node->Get(Node,AOUT_MUTE,&p->Mute,sizeof(bool_t));
- Node->Get(Node,AOUT_PAN,&p->Pan,sizeof(int));
- }
- return p;
- }
- static NOINLINE int UpdateAudioEx(player_base* p,node* Node)
- {
- if (Node)
- {
- Node->Set(Node,AOUT_STEREO,&p->Stereo,sizeof(int));
- Node->Set(Node,AOUT_QUALITY,&p->AudioQuality,sizeof(int));
- Node->Set(Node,AOUT_PREAMP,&p->PreAmp,sizeof(int));
- Node->Set(Node,AOUT_VOLUME,&p->Volume,sizeof(int));
- Node->Set(Node,AOUT_MUTE,&p->Mute,sizeof(bool_t));
- Node->Set(Node,AOUT_PAN,&p->Pan,sizeof(int));
- }
- return ERR_NONE;
- }
- static NOINLINE int UpdateAudio(player_base* p)
- {
- return UpdateAudioEx(p,p->AOutput);
- }
- static NOINLINE int UpdateVolume(player_base* p)
- {
- if (p->AOutput)
- p->AOutput->Set(p->AOutput,AOUT_VOLUME,&p->Volume,sizeof(int));
- return ERR_NONE;
- }
- static int UpdateStreams(player_base* p,int ForceRefresh,bool_t CodecSkip);
- static NOINLINE int UpdatePowerOff(player_base* p)
- {
- Notify(p,PLAYER_POWEROFF,p->PowerOff); // before poweroff is handled! (PalmOS may need to turn on clipping)
- #ifdef TARGET_WINCE
- if (p->PowerOff)
- {
- bool_t b;
- Lock(p,0);
- if (p->VOutput && p->VOutput->Get(p->VOutput,FLOW_BACKGROUND,&b,sizeof(b))==ERR_NONE)
- {
- int Old = p->VOutputId;
- bool_t OldAccel = p->VideoAccel;
- p->VOutputId = 0;
- UpdateStreams(p,-1,0);
- p->VOutputId = Old;
- p->VideoAccel = OldAccel;
- p->PowerOffReSync = 1;
- }
- if (p->Play || p->FFwd)
- {
- Lock(p,1);
- p->Play = 0;
- p->FFwd = 0;
- p->Fill = 1;
- SetPlay(p,0);
- Unlock(p,1);
- }
- Unlock(p,0);
- }
- else
- {
- if (p->PowerOffReSync)
- {
- p->PowerOffReSync = 0;
- UpdateStreams(p,-1,0);
- ReSync(p,1);
- }
- }
- #else
- if (p->PowerOff && (p->Play || p->FFwd || p->Fill))
- {
- Lock(p,1);
- p->Play = 0;
- p->FFwd = 0;
- p->Fill = 0; // do not fill, because acceleration devices could be turned off
- SetPlay(p,0); // calls UpdateBackground
- Unlock(p,1);
- }
- else
- if (!p->PowerOff && !p->Play && !p->FFwd && !p->Fill)
- {
- Lock(p,1);
- p->Fill = 1; // turn on fill
- SetPlay(p,0); // calls UpdateBackground
- Unlock(p,1);
- }
- else
- UpdateBackground(p);
- #endif
- return ERR_NONE;
- }
- static NOINLINE int UpdateForeground(player_base* p)
- {
- if (!p->Foreground)
- if ((IsVideo(p) && !p->KeepPlayVideo) ||
- (!IsVideo(p) && !p->KeepPlayAudio))
- {
- Lock(p,1);
- SetPlay(p,1);
- Unlock(p,1);
- }
- UpdateVideo(p,0);
- return ERR_NONE;
- }
- static int FindCurrentIndex(player_base* p)
- {
- int Index;
- for (Index=0;Index<p->PlayListCount;++Index)
- if (p->PlayIndex[Index] == p->Current)
- return Index;
- return 0;
- }
- static uint32_t Rand(player_base* p)
- {
- p->RndSeed = p->RndSeed*0x8088405U + 0x251001U;
- return p->RndSeed;
- }
- static int RandomizePlayIndex(player_base* p,bool_t CurrentAsFirst)
- {
- int No,n;
- int* Index = p->PlayIndex;
- for (No=0;No<p->PlayListCount;++No)
- Index[No] = No;
- if (p->Shuffle)
- {
- for (n=0;n<4;++n)
- for (No=0;No<p->PlayListCount;++No)
- SwapInt(&Index[No],&Index[Rand(p) % p->PlayListCount]);
- if (CurrentAsFirst)
- {
- // make sure current media is the first
- // so the full range of playlist will be played before it's regenerated
- No = FindCurrentIndex(p);
- if (No>0)
- SwapInt(&Index[No],&Index[0]);
- }
- }
- return ERR_NONE;
- }
- static int SetPlayListCount(player_base* p, int Count)
- {
- int i,j;
- int* Index = p->PlayIndex;
- item* List = (item*)realloc(p->PlayList,sizeof(item)*Count);
- if (!List && Count)
- return ERR_OUT_OF_MEMORY;
- if (p->PlayListCount > Count)
- {
- // remove unwanted indexes
- for (i=0,j=0;i<p->PlayListCount;++i)
- if (Index[i]<Count)
- Index[j++] = Index[i];
- assert(j==Count);
- }
- // realloc index
- Index = (int*)realloc(Index,sizeof(int)*Count);
- if (!Index && Count)
- {
- SetPlayListCount(p,0);
- return ERR_OUT_OF_MEMORY;
- }
- // add new items
- for (i=p->PlayListCount;i<Count;++i)
- {
- memset(List+i,0,sizeof(item));
- List[i].Length = -1;
- List[i].Changed = 1;
- Index[i] = i;
- if (p->Shuffle)
- SwapInt(&Index[i],&Index[Rand(p) % (i+1)]);
- }
- p->PlayIndex = Index;
- p->PlayList = List;
- p->PlayListCount = Count;
- if (p->Current >= Count)
- {
- p->Current = 0;
- p->CurrentChanged = 1;
- }
- NotifyList(p);
- return ERR_NONE;
- }
- static void StopLoadMode(player_base* p)
- {
- if (p->LoadMode)
- {
- DEBUG_MSG(DEBUG_TEST,T("LoadMode Leaving"));
- p->LoadMode = 0;
- UpdatePlay(p); // UpdateInputPriority(p) called too
- UpdateRunProcess(p);
- UpdateRunInput(p);
- Notify(p,PLAYER_LOADMODE,p->LoadMode);
- }
- }
- static void StopBench(player_base* p)
- {
- p->BenchTime = Scale(GetTimeTick() - p->BenchStart,TICKSPERSEC,GetTimeFreq());
- p->Bench = 0;
- p->MicroDrive = p->SaveMicroDrive;
- p->BenchSrc.x = p->BenchSrc.y = 0;
- p->BenchDst.x = p->BenchDst.y = 0;
- if (p->VOutput)
- {
- rect Rect;
- packetformat Format;
- if (p->VOutput->Get(p->VOutput,OUT_INPUT|PIN_FORMAT,&Format,sizeof(Format))==ERR_NONE)
- {
- p->BenchSrc.x = Format.Format.Video.Width;
- p->BenchSrc.y = Format.Format.Video.Height;
- }
- if (p->VOutput->Get(p->VOutput,VOUT_OUTPUTRECT,&Rect,sizeof(Rect))==ERR_NONE)
- {
- p->BenchDst.x = Rect.Width;
- p->BenchDst.y = Rect.Height;
- }
- }
- UpdateFormat(p,0);
- UpdatePlay(p);
- UpdateSpeed(p);
- Flush(p);
- Notify(p,PLAYER_BENCHMARK,p->BenchTime);
- }
- static void UpdateKeepAlive(player_base* p)
- {
- bool_t KeepAlive = p->FFwd || p->Play;
- if (p->VOutput)
- p->VOutput->Set(p->VOutput,OUT_KEEPALIVE,&KeepAlive,sizeof(KeepAlive));
- if (p->AOutput)
- p->AOutput->Set(p->AOutput,OUT_KEEPALIVE,&KeepAlive,sizeof(KeepAlive));
- }
- static int SetPlay(player_base* p,bool_t Stop)
- {
- bool_t Refresh;
- if (Stop)
- {
- p->Play = 0;
- p->FFwd = 0;
- p->Fill = 0;
- p->Sync = 0;
- p->InSeek = 0;
- if (p->TemporaryHidden)
- {
- p->TemporaryHidden = 0;
- UpdateVideo(p,-1);
- }
- }
- DEBUG_MSG2(DEBUG_PLAYER,T("Play %d,%d"),p->Play,p->FFwd);
- StopLoadMode(p);
- if (p->Bench && !p->Play)
- StopBench(p);
- if (p->Format && p->Input && p->Timer)
- {
- if (p->NoMoreInput && (p->Play || p->FFwd) && p->IOError)
- {
- p->Fill = 1;
- p->NoMoreInput = 0;
- p->IOError = 0;
- UpdateRunInput(p);
- }
- if (p->TimerPlay)
- {
- UpdateKeepAlive(p);
- UpdatePlay(p);
- UpdateSpeed(p);
- }
- else
- {
- UpdateSpeed(p);
- UpdatePlay(p);
- UpdateKeepAlive(p);
- }
- UpdateRunProcess(p);
- // make sure current frame is show after pausing a dropping period
- Refresh = p->VOutput && !p->Play && !p->FFwd && !p->Sync && p->Fill;
- if (Refresh && p->FlowBuffer)
- Refresh = p->FlowBuffer->Set(p->FlowBuffer,FLOW_RESEND,NULL,0) != ERR_NONE;
- if (Refresh)
- ReSync(p,0);
- }
- else
- UpdateKeepAlive(p);
- UpdateTimeouts(p);
- UpdateBackground(p);
- Notify(p,PLAYER_PLAY,p->Play);
- return ERR_NONE;
- }
- static int Seek2(player_base* p,tick_t Time,const fraction* Percent,bool_t PrevKey)
- {
- tick_t Duration;
- int Result = ERR_INVALID_DATA;
- if (p->Format && p->Input && p->Timer)
- {
- int FileSize;
- int FilePos = -1;
- if (Percent)
- {
- if (p->Format->Get(p->Format,FORMAT_DURATION,&Duration,sizeof(Duration))==ERR_NONE)
- {
- Time = Scale(Duration,Percent->Num,Percent->Den);
- if (Time < 0)
- Time = 0;
- }
- else
- if (p->Input->Get(p->Input,STREAM_LENGTH,&FileSize,sizeof(FileSize))==ERR_NONE)
- {
- Time = -1;
- FilePos = Scale(FileSize,Percent->Num,Percent->Den);
- if (FilePos < 0)
- FilePos = 0;
- }
- else
- return ERR_NOT_SUPPORTED;
- }
- p->SeekAfterSync.Num = -1;
- DEBUG_MSG2(DEBUG_PLAYER,T("Sync time:%d filepos:%d"),Time,FilePos);
- Result = p->Format->Sync(p->Format,Time,FilePos,PrevKey);
- if (Result == ERR_LOADING_HEADER && p->Sync && Percent)
- {
- p->SeekAfterSync = *Percent;
- p->SeekAfterSyncPrevKey = PrevKey;
- Result = ERR_NONE;
- }
- else
- if (Result == ERR_NONE)
- {
- Flush(p);
- p->NoMoreInput = 0;
- p->BufferMax = p->CurrBufferSize2;
- p->Position = Time;
- p->Sync = 1;
- p->PosNotify = -1;
- StopLoadMode(p);
- UpdatePlay(p);
- UpdateRunProcess(p);
- UpdateRunInput(p);
- // wakeup input threads (if needed)
- #ifdef MULTITHREAD
- EventSet(p->EventProcess);
- #else
- p->WaitForProcess = 0;
- #endif
- }
- Notify(p,PLAYER_PERCENT,0);
- }
- else
- if (!p->Wnd && Percent)
- {
- p->SeekAfterSync = *Percent;
- p->SeekAfterSyncPrevKey = 1;
- Result = ERR_NONE;
- }
- return Result;
- }
- static int InSeek(player_base* p,tick_t Time,const fraction* Percent,bool_t PrevKey)
- {
- int Result;
- #ifdef MULTITHREAD
- assert(p->ProcessLocked != ThreadId() && p->InputLocked != ThreadId());
- #endif
- LockEnter(p->Lock);
- if (p->InSeek)
- {
- tick_t Duration;
- fraction Pos;
- if (Percent)
- Pos = *Percent;
- else
- if (p->Format->Get(p->Format,FORMAT_DURATION,&Duration,sizeof(Duration))==ERR_NONE)
- {
- Pos.Num = Time;
- Pos.Den = Duration;
- }
- else
- {
- Pos.Num = -1;
- Pos.Den = 0;
- }
- if (EqFrac(&p->InSeekPos,&Pos))
- {
- LockLeave(p->Lock);
- return ERR_NONE;
- }
- p->InSeekPos = Pos;
- if (p->InSeekPos.Num>=0 && (p->Sync || !IsVideo(p)))
- {
- p->SeekAfterSync = p->InSeekPos;
- p->SeekAfterSyncPrevKey = PrevKey;
- Notify(p,PLAYER_PERCENT,0);
- LockLeave(p->Lock);
- return ERR_NONE;
- }
- }
- Lock(p,1);
- Result = Seek2(p,Time,Percent,PrevKey);
- Unlock(p,1);
- LockLeave(p->Lock);
- return Result;
- }
- static void ReSync(player_base* p,bool_t DoSeek)
- {
- if (p->Format)
- {
- if (DoSeek && p->Position>=0)
- Seek2(p,p->Position,NULL,1);
- else
- if (p->Format->Sync(p->Format,-1,-1,0) == ERR_NONE)
- {
- p->Sync = 1;
- p->PosNotify = -1;
- StopLoadMode(p);
- UpdatePlay(p);
- UpdateRunProcess(p);
- // wakeup input threads, just in case
- #ifdef MULTITHREAD
- EventSet(p->EventProcess);
- #else
- p->WaitForProcess = 0;
- #endif
- }
- }
- }
- static bool_t SupportVQNode(node* Node)
- {
- int Shift;
- bool_t Buffered;
- if (NodeIsClass(Node->Class,IDCT_CLASS))
- {
- if (Node->Get(Node,IDCT_SHIFT,&Shift,sizeof(Shift))!=ERR_NONE)
- return 0;
- }
- else
- if (Node->Get(Node,FLOW_BUFFERED,&Buffered,sizeof(Buffered))==ERR_NONE && Buffered)
- return 0;
- return 1;
- }
- static bool_t SupportVQ(player_base* p)
- {
- const ref *i;
- for (i=ARRAYBEGIN(p->Ref,ref);i!=ARRAYEND(p->Ref,ref);++i)
- if (!SupportVQNode(i->Node))
- return 0;
- if (p->FlowBuffer && !SupportVQNode(p->FlowBuffer)) // flowbuffer may not be in ref array (accel. idct)
- return 0;
- return 1;
- }
- static int UpdateVideoQuality(player_base* p,node* Node)
- {
- if (NodeIsClass(Node->Class,IDCT_CLASS))
- {
- int Shift = 2-p->VideoQuality;
- Node->Set(Node,IDCT_SHIFT,&Shift,sizeof(Shift));
- }
- return ERR_NONE;
- }
- static void ClearStats(player_base* p)
- {
- int Zero=0;
- if (p->VOutput)
- {
- p->VOutput->Set(p->VOutput,OUT_TOTAL,&Zero,sizeof(Zero));
- p->VOutput->Set(p->VOutput,OUT_DROPPED,&Zero,sizeof(Zero));
- }
- if (p->AOutput)
- {
- p->AOutput->Set(p->AOutput,OUT_TOTAL,&Zero,sizeof(Zero));
- p->AOutput->Set(p->AOutput,OUT_DROPPED,&Zero,sizeof(Zero));
- }
- }
- static void StartBench(player_base* p)
- {
- int Used;
- p->SaveMicroDrive = p->MicroDrive;
- p->MicroDrive = 0;
- p->InSeek = 0;
- UpdateFormat(p,0);
- if (!QueryAdvanced(ADVANCED_BENCHFROMPOS))
- Seek2(p,0,NULL,0);
- p->BufferMax = p->CurrBufferSize2;
- while (p->Format->Read(p->Format,p->BufferMax,&Used) == ERR_NONE &&
- Used < p->UsedEnough2);
- ClearStats(p);
- p->Bench = 1;
- p->BenchStart = GetTimeTick();
- p->Play = 1;
- p->FFwd = 0;
- p->Fill = 0;
- SetPlay(p,0);
- }
- static void SetTimerNode(player_base* p,node* Timer,bool_t SaveTime)
- {
- tick_t Time = 0;
- if (p->Timer && SaveTime)
- p->Timer->Get(p->Timer,TIMER_TIME,&Time,sizeof(Time));
- p->Timer = Timer;
- if (p->Timer)
- {
- p->Timer->Set(p->Timer,TIMER_SPEED,&p->Speed,sizeof(p->Speed));
- p->Timer->Set(p->Timer,TIMER_TIME,&Time,sizeof(Time));
- UpdatePlay(p);
- }
- UpdateRunProcess(p);
- }
- static node* AddRef(player_base* p,node* Node);
- static void ReleaseRef(player_base* p,node* Node);
- static bool_t UpdateNode(player_base* p,node* Node);
- static void ReleaseNode(player_base* p,node* Node);
- static bool_t UpdateOutput(player_base* p,node* From,const datadef* FromDef,int* FoundFirst);
- static NOINLINE void NullPin(node* p,int No)
- {
- pin Pin;
- Pin.Node = NULL;
- Pin.No = 0;
- p->Set(p,No,&Pin,sizeof(Pin));
- }
- static void TryComment(player_base* p,node* From,const datadef* FromDef)
- {
- pin Pin;
- Pin.Node = (node*)p;
- Pin.No = PLAYER_COMMENT+p->StreamNo;
- From->Set(From,FromDef->No,&Pin,sizeof(Pin));
- }
- static void ReleaseOutput(player_base* p,node* Node,datadef* DataDef)
- {
- node* Null = NULL;
- pin Pin;
- node* Ptr;
- bool_t RdOnly = (DataDef->Flags & DF_RDONLY)!=0;
- switch (DataDef->Type)
- {
- case TYPE_NODE:
- if (Node->Get(Node,DataDef->No,&Ptr,sizeof(Ptr))==ERR_NONE && Ptr)
- {
- ReleaseNode(p,Ptr);
- if (!RdOnly)
- {
- Node->Set(Node,DataDef->No,&Null,sizeof(Null));
- ReleaseRef(p,Ptr);
- }
- }
- break;
- case TYPE_COMMENT:
- if (!RdOnly)
- NullPin(Node,DataDef->No);
- break;
- case TYPE_PACKET:
- if (Node->Get(Node,DataDef->No,&Pin,sizeof(Pin))==ERR_NONE && Pin.Node)
- {
- ReleaseNode(p,Pin.Node);
- if (!RdOnly)
- {
- Disconnect(Node,DataDef->No,Pin.Node,Pin.No);
- ReleaseRef(p,Pin.Node);
- }
- }
- }
- }
- static void ReleaseNode(player_base* p,node* Node)
- {
- if (Node)
- {
- bool_t False = 0;
- datadef DataDef;
- int No;
- if (p->VOutput == Node)
- {
- p->VOutput = NULL;
- p->VIDCT = NULL;
- }
- if (p->AOutput == Node)
- {
- SetTimerNode(p,NodeEnumObject(NULL,SYSTIMER_ID),1); // restore system timer
- p->AOutput = NULL;
- }
- if (p->FlowBuffer == Node)
- p->FlowBuffer = NULL;
- Node->Set(Node,FLOW_BACKGROUND,&False,sizeof(bool_t));
- for (No=0;NodeEnum(Node,No,&DataDef)==ERR_NONE;++No)
- if (DataDef.Flags & DF_OUTPUT)
- ReleaseOutput(p,Node,&DataDef);
- }
- }
- static node* GetNode(player_base* p,int Class)
- {
- node* Node = NULL;
- bool_t ReUse = 0;
- bool_t IDCT = 0;
- if (Class == (int)VOUT_IDCT_CLASS(p->VOutputId) && NodeIsClass(Class,IDCT_CLASS))
- {
- Class = p->VOutputId;
- IDCT = 1;
- }
- if (Class == p->AOutputId || Class == p->VOutputId)
- {
- ref *i;
- for (i=ARRAYBEGIN(p->Ref,ref);i!=ARRAYEND(p->Ref,ref);++i)
- if (i->Node->Class == Class)
- {
- Node = i->Node;
- ReUse = 1;
- break;
- }
- }
- if (!Node)
- {
- Node = NodeCreate(Class);
- if (Node)
- {
- if (NodeIsClass(Class,VOUT_CLASS))
- UpdateVideoEx(p,Node,-1,0);
- if (NodeIsClass(Class,AOUT_CLASS))
- UpdateAudioEx(p,Node);
- UpdateVideoQuality(p,Node);
- }
- }
- if (Node)
- {
- pin Pin;
- if (IDCT)
- {
- node* i;
- Node->Get(Node,VOUT_IDCT,&i,sizeof(i));
- if (!i)
- {
- if (!ReUse)
- NodeDelete(Node);
- return NULL;
- }
- Node = i;
- }
- Pin.No = PLAYER_NOT_SUPPORTED_DATA;
- Pin.Node = (node*)p;
- Node->Set(Node,FLOW_NOT_SUPPORTED,&Pin,sizeof(Pin));
- AddRef(p,Node);
- }
- return Node;
- }
- static bool_t TryPin(player_base* p,node* From,int FromNo,int ToClass,int* FoundFirst,int Failed)
- {
- node* To;
- if (ToClass == Failed)
- return 0;
- To = GetNode(p,ToClass);
- if (To)
- {
- packetformat Format;
- datadef ToDef;
- int No;
- int Result;
- for (No=0;NodeEnum(To,No,&ToDef)==ERR_NONE;++No)
- if (ToDef.Type == TYPE_PACKET && (ToDef.Flags & DF_INPUT) && !(ToDef.Flags & DF_RDONLY))
- {
- if (To->Get(To,ToDef.No|PIN_FORMAT,&Format,sizeof(Format))==ERR_NONE && Format.Type != PACKET_NONE)
- continue; // already connected
- Result = ConnectionUpdate(From,FromNo,To,ToDef.No);
- if (Result == ERR_NOT_SUPPORTED && FoundFirst)
- *FoundFirst = 2; // found codec, but this type is not supported
- else
- if (Result == ERR_NONE)
- {
- if (FoundFirst)
- *FoundFirst = 1;
- if (UpdateNode(p,To))
- return 1;
- }
- Disconnect(From,FromNo,To,ToDef.No);
- }
- ReleaseRef(p,To);
- }
- return 0;
- }
- static bool_t IsCodecSkip(player_base* p,int Id)
- {
- int i;
- for (i=0;i<p->CodecSkipCount;++i)
- if (p->CodecSkip[i]==Id)
- return 1;
- return 0;
- }
- static bool_t UpdateOutput(player_base* p,node* From,const datadef* FromDef,int* FoundFirst)
- {
- packetformat Format;
- bool_t EqMissing;
- node* Null = NULL;
- node* To;
- node* Old = NULL;
- int Force = 0;
- int Skip = 0;
- pin Pin;
- bool_t RdOnly = (FromDef->Flags & DF_RDONLY)!=0;
- int Failed = 0;
- if (FoundFirst)
- *FoundFirst = 0;
- switch (FromDef->Type)
- {
- case TYPE_NODE:
- if (!RdOnly)
- switch (FromDef->Format1)
- {
- case IDCT_CLASS:
- if (VOutIDCT(p->VOutputId))
- {
- int IDCT = VOUT_IDCT_CLASS(p->VOutputId);
- if (p->VideoAccel && p->Timing && p->SkipAccelFrom != From->Class)
- Force = IDCT;
- else
- Skip = IDCT;
- }
- break;
- case VOUT_CLASS:
- Force = p->VOutputId;
- break;
- case AOUT_CLASS:
- Force = p->AOutputId;
- break;
- }
- // already connected pointer?
- if (From->Get(From,FromDef->No,&Old,sizeof(Old))==ERR_NONE && Old)
- {
- if (FoundFirst)
- *FoundFirst = 1;
- if ((!Force || Old->Class == Force) && (!Skip || Old->Class != Skip))
- {
- if (UpdateNode(p,Old))
- return 1;
- Failed = Old->Class;
- }
- else
- ReleaseNode(p,Old);
- }
- if (RdOnly)
- return 0;
- if (Force)
- {
- To = GetNode(p,Force);
- if (To && From->Set(From,FromDef->No,&To,sizeof(To))==ERR_NONE)
- {
- ReleaseRef(p,Old);
- Old = To;
- if (FoundFirst)
- *FoundFirst = 1;
- if (UpdateNode(p,To))
- return 1;
- }
- else
- ReleaseRef(p,To);
- if (FromDef->Format1 == IDCT_CLASS)
- p->SkipAccelFrom = From->Class;
- if (Old && !Failed && UpdateNode(p,Old))
- return 1;
- }
- if (FromDef->Format1 != VOUT_CLASS && FromDef->Format1 != AOUT_CLASS)
- {
- int* i;
- array List;
- NodeEnumClass(&List,FromDef->Format1);
- for (i=ARRAYBEGIN(List,int);i!=ARRAYEND(List,int);++i)
- if (Failed != *i && (To = GetNode(p,*i))!=NULL)
- {
- if (From->Set(From,FromDef->No,&To,sizeof(To))==ERR_NONE)
- {
- ReleaseRef(p,Old);
- Old = To;
- if (FoundFirst)
- *FoundFirst = 1;
- if (UpdateNode(p,To))
- {
- ArrayClear(&List);
- return 1;
- }
- }
- else
- ReleaseRef(p,To);
- }
- ArrayClear(&List);
- }
- From->Set(From,FromDef->No,&Null,sizeof(Null));
- ReleaseRef(p,Old);
- break;
- case TYPE_COMMENT:
- if (!RdOnly)
- TryComment(p,From,FromDef);
- return 1;
- case TYPE_PACKET:
- EqMissing = p->Eq && !Used(p,p->Equalizer);
- // already connected pin?
- if (From->Get(From,FromDef->No,&Pin,sizeof(Pin))==ERR_NONE && Pin.Node)
- {
- if (!IsCodecSkip(p,Pin.Node->Class) && (!NodeIsClass(Pin.Node->Class,OUT_CLASS) || Pin.Node->Class == p->VOutputId ||
- (Pin.Node->Class == p->AOutputId && !EqMissing)))
- {
- if (FoundFirst)
- *FoundFirst = 1;
- if (UpdateNode(p,Pin.Node))
- return 1;
- Failed = Pin.Node->Class; // don't try with class again
- }
- else
- ReleaseNode(p,Pin.Node);
- if (!RdOnly)
- {
- Disconnect(From,FromDef->No,Pin.Node,Pin.No);
- ReleaseRef(p,Pin.Node);
- }
- }
- if (RdOnly)
- return 0;
- if (From->Get(From,FromDef->No|PIN_FORMAT,&Format,sizeof(Format))==ERR_NONE)
- {
- array List;
- const int *i;
- if (Format.Type == PACKET_VIDEO)
- {
- if (!p->VOutputId)
- {
- if (FoundFirst)
- *FoundFirst = 1;
- return 0;
- }
- if (TryPin(p,From,FromDef->No,p->VOutputId,FoundFirst,Failed))
- return 1;
- }
- if (Format.Type == PACKET_AUDIO && !EqMissing)
- {
- if (!p->AOutputId)
- {
- if (FoundFirst)
- *FoundFirst = 1;
- return 0;
- }
- if (TryPin(p,From,FromDef->No,p->AOutputId,FoundFirst,Failed))
- return 1;
- }
- PacketFormatEnumClass(&List,&Format);
- for (i=ARRAYBEGIN(List,int);i!=ARRAYEND(List,int);++i)
- if (!IsCodecSkip(p,*i) && TryPin(p,From,FromDef->No,*i,FoundFirst,Failed))
- {
- ArrayClear(&List);
- return 1;
- }
- if (Format.Type == PACKET_AUDIO && EqMissing)
- {
- if (!p->AOutputId)
- {
- if (FoundFirst)
- *FoundFirst = 1;
- return 0;
- }
- if (TryPin(p,From,FromDef->No,p->AOutputId,FoundFirst,Failed))
- return 1;
- }
- ArrayClear(&List);
- }
- break;
- }
- return 0;
- }
- static node* AddRef(player_base* p,node* Node)
- {
- if (Node)
- {
- bool_t Found;
- ref Ref;
- int No;
- Ref.RefCount = 1;
- if (Node->Get(Node,NODE_PARTOF,&Ref.Node,sizeof(node*)) != ERR_NONE)
- Ref.Node = Node;
- No = ArrayFind(&p->Ref,ARRAYCOUNT(p->Ref,ref),sizeof(ref),&Ref,(arraycmp)CmpRef,&Found);
- if (!Found)
- ArrayAdd(&p->Ref,ARRAYCOUNT(p->Ref,ref),sizeof(ref),&Ref,(arraycmp)CmpRef,64);
- else
- ARRAYBEGIN(p->Ref,ref)[No].RefCount++;
- }
- return Node;
- }
- static void ReleaseRef(player_base* p,node* Node)
- {
- if (Node)
- {
- int No;
- ref Ref;
- bool_t Found;
- if (Node->Get(Node,NODE_PARTOF,&Ref.Node,sizeof(node*)) != ERR_NONE)
- Ref.Node = Node;
- No = ArrayFind(&p->Ref,ARRAYCOUNT(p->Ref,ref),sizeof(ref),&Ref,(arraycmp)CmpRef,&Found);
- if (Found && --(ARRAYBEGIN(p->Ref,ref)[No].RefCount)==0)
- {
- ArrayRemove(&p->Ref,ARRAYCOUNT(p->Ref,ref),sizeof(ref),&Ref,(arraycmp)CmpRef);
- NodeDelete(Ref.Node);
- }
- }
- }
- static bool_t UpdateNode(player_base* p,node* Node)
- {
- bool_t Buffered;
- assert(NodeIsClass(Node->Class,FLOW_CLASS));
- if (!NodeIsClass(Node->Class,OUT_CLASS))
- {
- // update output pins
- datadef DataDef;
- int No;
- for (No=0;NodeEnum(Node,No,&DataDef)==ERR_NONE;++No)
- if (DataDef.Flags & DF_OUTPUT)
- if (!UpdateOutput(p,Node,&DataDef,NULL))
- {
- ReleaseNode(p,Node);
- return 0;
- }
- }
- if (Node->Class == p->VOutputId)
- {
- p->VOutput = Node;
- Node->Set(Node,VOUT_PLAY,&p->TimerPlay,sizeof(bool_t));
- Node->Get(Node,VOUT_IDCT,&p->VIDCT,sizeof(p->VIDCT));
- if (!p->VIDCT)
- p->VideoAccel = 0;
- if (p->FullScreenAfterSync && !p->FullScreen)
- Notify(p,PLAYER_FULLSCREEN,1);
- }
- if (Node->Class == p->AOutputId)
- {
- node* Timer;
- p->AOutput = Node;
- if (Node->Get(Node,AOUT_TIMER,&Timer,sizeof(Timer))==ERR_NONE)
- SetTimerNode(p,Timer,1);
- }
- if (Node->Get(Node,FLOW_BUFFERED,&Buffered,sizeof(Buffered))==ERR_NONE && Buffered)
- p->FlowBuffer = Node;
- return 1;
- }
- static int ReleaseStreamsNotify(player_base* p,int Param,int Param2)
- {
- datadef DataDef;
- int No;
- for (No=0;NodeEnum((node*)p->Format,No,&DataDef)==ERR_NONE;++No)
- {
- if (DataDef.No >= FORMAT_COMMENT && DataDef.No < FORMAT_COMMENT+MAXSTREAM)
- NullPin((node*)p->Format,DataDef.No);
- if (DataDef.No >= FORMAT_STREAM && DataDef.No < FORMAT_STREAM+MAXSTREAM)
- ReleaseOutput(p,(node*)p->Format,&DataDef);
- }
- return ERR_NONE;
- }
- static int UpdateStreamsMask(player_base* p)
- {
- int Result = UpdateStreams(p,1,0);
- if (Result == ERR_NONE && p->Format)
- {
- tick_t Time;
- p->Timer->Get(p->Timer,TIMER_TIME,&Time,sizeof(tick_t));
- p->Format->Set(p->Format,FORMAT_UPDATEMASK,&Time,sizeof(tick_t));
- }
- return Result;
- }
- static int UpdateStreamsNotify(player_base* p,int Param,int Param2)
- {
- // called by process thread
- // we are inside the LockProcess so Lock/Unlock not needed
- // inputprocess doesn't bother us here
- UpdateStreams(p,-1,0);
- return ERR_NONE;
- }
- static void ReleaseFormat(player_base* p)
- {
- NullPin((node*)p->Format,FORMAT_GLOBAL_COMMENT);
- p->Format->Set(p->Format,FORMAT_INPUT,NULL,0);
- NodeDelete((node*)p->Format);
- p->Format = NULL;
- }
- static void ErrorCodec(packetformat* Format)
- {
- tchar_t Name[64];
- int Id = 0;
- switch (Format->Type)
- {
- case PACKET_VIDEO:
- Id = PLAYER_VIDEO_NOT_FOUND;
- break;
- case PACKET_AUDIO:
- #ifdef CONFIG_GPL
- if (Format->Format.Audio.Format == AUDIOFMT_AAC)
- Id = PLAYER_AUDIO_NOT_FOUND2;
- else
- #endif
- Id = PLAYER_AUDIO_NOT_FOUND;
- break;
- }
- if (Id && PacketFormatName(Format,Name,TSIZEOF(Name)))
- ShowError(PLAYER_ID,PLAYER_ID,Id,Name);
- }
- static int UpdateStreams(player_base* p,int ForceRefresh,bool_t CodecSkip)
- {
- packetformat Format;
- datadef DataDef;
- int FoundCodec;
- int No;
- p->UpdateStreamsNeeded = 0;
- if (!p->Format)
- return ERR_INVALID_PARAM;
- // first release unused streams
- for (No=0;NodeEnum((node*)p->Format,No,&DataDef)==ERR_NONE;++No)
- if (DataDef.No >= FORMAT_STREAM && DataDef.No < FORMAT_STREAM+MAXSTREAM)
- {
- p->StreamNo = DataDef.No - FORMAT_STREAM;
- if (!p->StreamSkip[p->StreamNo] &&
- p->Format->Get(p->Format,DataDef.No|PIN_FORMAT,&Format,sizeof(Format))==ERR_NONE &&
- Format.Type != PACKET_NONE && p->Selected[Format.Type]>=0 && p->Selected[Format.Type] != p->StreamNo)
- ReleaseOutput(p,(node*)p->Format,&DataDef);
- }
- for (No=0;NodeEnum((node*)p->Format,No,&DataDef)==ERR_NONE;++No)
- {
- if (DataDef.No >= FORMAT_COMMENT && DataDef.No < FORMAT_COMMENT+MAXSTREAM)
- {
- p->StreamNo = DataDef.No - FORMAT_COMMENT;
- TryComment(p,(node*)p->Format,&DataDef);
- }
- if (DataDef.No >= FORMAT_STREAM && DataDef.No < FORMAT_STREAM+MAXSTREAM)
- {
- p->StreamNo = DataDef.No - FORMAT_STREAM;
- if (!p->StreamSkip[p->StreamNo] &&
- p->Format->Get(p->Format,DataDef.No|PIN_FORMAT,&Format,sizeof(Format))==ERR_NONE &&
- Format.Type != PACKET_NONE)
- {
- if (p->Selected[Format.Type]<0)
- p->Selected[Format.Type] = p->StreamNo;
- if (p->Selected[Format.Type] == p->StreamNo &&
- !UpdateOutput(p,(node*)p->Format,&DataDef,&FoundCodec) && FoundCodec!=1)
- {
- if (!FoundCodec && !CodecSkip)
- ErrorCodec(&Format);
- p->Selected[Format.Type] = -1;
- p->StreamSkip[p->StreamNo] = 1;
- }
- }
- }
- }
- UpdateVideo(p,ForceRefresh);
- UpdateTimeouts(p);
- return ERR_NONE;
- }
- static int UpdateFormat(player_base* p,bool_t BufferChanged)
- {
- if (BufferChanged)
- p->BufferWarning = 0;
- p->CurrBufferSize2 = p->MicroDrive ? p->MDBufferSize2 : p->BufferSize2;
- p->BufferMax = p->CurrBufferSize2;
- if (p->Format)
- p->Format->Set(p->Format,FORMAT_BUFFERSIZE,&p->CurrBufferSize2,sizeof(int));
- return ERR_NONE;
- }
- static void RemoveFilter(player_base* p,node* Filter,int In,int Out)
- {
- pin InPin;
- pin OutPin;
- if (Filter->Get(Filter,In,&InPin,sizeof(InPin))==ERR_NONE && InPin.Node &&
- Filter->Get(Filter,Out,&OutPin,sizeof(OutPin))==ERR_NONE && OutPin.Node)
- {
- if (ConnectionUpdate(InPin.Node,InPin.No,OutPin.Node,OutPin.No)==ERR_NONE)
- {
- ConnectionUpdate(NULL,0,Filter,Out);
- ConnectionUpdate(Filter,In,NULL,0);
- ReleaseRef(p,Filter);
- }
- else
- {
- ConnectionUpdate(InPin.Node,InPin.No,Filter,In);
- ConnectionUpdate(Filter,Out,OutPin.Node,OutPin.No);
- }
- }
- }
- static void InsertFilter(player_base* p,node* Filter,int In,int Out,node* Output,int OutputIn)
- {
- pin Pin;
- if (Output->Get(Output,OutputIn,&Pin,sizeof(Pin))==ERR_NONE && Pin.Node)
- {
- if (ConnectionUpdate(Pin.Node,Pin.No,Filter,In)==ERR_NONE &&
- ConnectionUpdate(Filter,Out,Output,OutputIn)==ERR_NONE)
- GetNode(p,Filter->Class);
- else
- {
- ConnectionUpdate(Pin.Node,Pin.No,Output,OutputIn); //try restoring...
- ConnectionUpdate(NULL,0,Filter,Out);
- ConnectionUpdate(Filter,In,NULL,0);
- }
- }
- }
- static int UpdateEqualizer(player_base* p)
- {
- if (p->Equalizer)
- p->Equalizer->Get(p->Equalizer,EQUALIZER_ENABLED,&p->Eq,sizeof(p->Eq));
- if (!p->Eq && Used(p,p->Equalizer))
- RemoveFilter(p,p->Equalizer,CODEC_INPUT,CODEC_OUTPUT);
- else
- if (p->Eq && !Used(p,p->Equalizer) && p->AOutput)
- InsertFilter(p,p->Equalizer,CODEC_INPUT,CODEC_OUTPUT,p->AOutput,OUT_INPUT);
- return ERR_NONE;
- }
- static void Unload(player_base* p,bool_t KeepPlay,bool_t KeepStreams,bool_t Refresh)
- {
- p->Sync = 0;
- p->Fill = 0;
- p->InSeek = 0;
- p->SeekAfterSync.Num = -1;
- if (!KeepPlay)
- SetPlay(p,1);
- if (!KeepStreams || p->Format)
- {
- int i;
- for (i=0;i<PACKET_MAX;++i)
- p->Selected[i] = -1;
- }
- if (p->Format)
- ReleaseFormat(p);
- if (p->Input)
- {
- NodeDelete((node*)p->Input);
- p->Input = NULL;
- }
- if (p->InputBase)
- {
- NodeDelete((node*)p->InputBase);
- p->InputBase = NULL;
- }
- p->Title[0] = 0;
- Notify(p,PLAYER_TITLE,1);
- assert(ARRAYEMPTY(p->Ref));
- ArrayClear(&p->Ref);
- ArrayClear(&p->Comment);
- p->Position = -1;
- p->NoMoreInput = 0;
- p->CurrentChanged = 1;
- p->BufferWarning = 0;
- p->CodecSkipCount = 0;
- if (!KeepPlay)
- UpdateTimeouts(p);
- UpdateRunInput(p);
- UpdateRunProcess(p);
- if (Refresh)
- Notify(p,PLAYER_PERCENT,1);
- }
- static void URLToTitle(tchar_t* Title, int TitleLen, const tchar_t* URL)
- {
- tchar_t Ext[MAXPATH];
- tchar_t *i,*j;
- bool_t HasHost;
- i = (tchar_t*) GetMime(URL,NULL,0,&HasHost);
- if (i==URL || !HasHost || tcschr(i,'/') || tcschr(i,'\'))
- SplitURL(URL,NULL,0,NULL,0,Title,TitleLen,Ext,TSIZEOF(Ext));
- else
- tcscpy_s(Title,TitleLen,URL);
- // replace %20 and '_' with space
- for (j=i=Title;*i;++i)
- {
- if (*i=='_')
- *j++ = ' ';
- else
- if (i[0]=='%' && i[1]=='2' && i[2]=='0')
- {
- *j++ = ' ';
- i += 2;
- }
- else
- *j++ = *i;
- }
- *j=0;
- }
- static bool_t LoadPlaylist(player* Player,const array* List,stream* Input,int* Index,const tchar_t* Path,const void* Probe,int ProbeSize)
- {
- int *i;
- bool_t SeekHead = 0;
- for (i=ARRAYBEGIN(*List,int);i!=ARRAYEND(*List,int);++i)
- {
- playlist* Playlist = (playlist*)NodeCreate(*i);
- if (Playlist)
- {
- tchar_t Base[MAXPATH];
- tchar_t Abs[MAXPATH];
- tchar_t ItemPath[MAXPATH];
- tchar_t ItemTitle[256];
- tick_t ItemLength;
- int Pos = *Index;
- if (SeekHead)
- {
- Input->Seek(Input,ProbeSize,SEEK_SET);
- SeekHead = 0;
- }
- if (Probe)
- Playlist->Set(Playlist,PLAYLIST_DATAFEED,Probe,ProbeSize);
- if (Playlist->Set(Playlist,PLAYLIST_STREAM,&Input,sizeof(Input)) == ERR_NONE)
- {
- SplitURL(Path,Base,TSIZEOF(Base),Base,TSIZEOF(Base),NULL,0,NULL,0);
- while (Playlist->ReadList(Playlist,ItemPath,TSIZEOF(ItemPath),ItemTitle,TSIZEOF(ItemTitle),&ItemLength)==ERR_NONE)
- {
- AbsPath(Abs,TSIZEOF(Abs),ItemPath,Base);
- Player->Set(Player,PLAYER_LIST_URL+Pos,Abs,sizeof(Abs));
- Player->Set(Player,PLAYER_LIST_TITLE+Pos,ItemTitle,sizeof(ItemTitle));
- Player->Set(Player,PLAYER_LIST_LENGTH+Pos,&ItemLength,sizeof(ItemLength));
- ++Pos;
- }
- SeekHead = 1;
- }
- Playlist->Set(Playlist,PLAYLIST_STREAM,NULL,0);
- NodeDelete((node*)Playlist);
- if (Pos != *Index)
- {
- *Index = Pos;
- return 1;
- }
- }
- }
- if (Probe) // restore file position
- Input->Seek(Input,ProbeSize,SEEK_SET);
- return 0;
- }
- static void ListSwap(player_base* p,int a,int b)
- {
- #ifdef MULTITHREAD
- assert(p->ProcessLocked != ThreadId() && p->InputLocked != ThreadId());
- #endif
- LockEnter(p->Lock);
- if (a < p->PlayListCount && b < p->PlayListCount && a!=b)
- {
- item Tmp;
- Tmp = p->PlayList[a];
- p->PlayList[a] = p->PlayList[b];
- p->PlayList[b] = Tmp;
- if (p->Current == a)
- p->Current = b;
- else
- if (p->Current == b)
- p->Current = a;
- p->PlayList[a].Changed = 1;
- p->PlayList[b].Changed = 1;
- NotifyList(p);
- }
- LockLeave(p->Lock);
- }
- static int Load(player_base* p,bool_t Silent,bool_t OnlyLocal,bool_t Nested);
- static bool_t ReDirect(player_base* p,const tchar_t* New,const tchar_t* Orig,bool_t Silent,bool_t OnlyLocal,bool_t Nested,int* Result)
- {
- tchar_t Base[MAXPATH];
- tchar_t Target[MAXPATH];
- SplitURL(Orig,Base,TSIZEOF(Base),Base,TSIZEOF(Base),NULL,0,NULL,0);
- AbsPath(Target,TSIZEOF(Target),New,Base);
- if (tcsicmp(Target,Orig)==0)
- return 0;
- p->Player.Set(p,PLAYER_LIST_URL+p->Current,Target,sizeof(Target));
- *Result = Load(p,Silent,OnlyLocal,Nested);
- return 1;
- }
- static int Load(player_base* p,bool_t Silent,bool_t OnlyLocal,bool_t Nested)
- {
- tchar_t ContentType[MAXPATH];
- array List;
- block Probe;
- tchar_t* URL;
- stream* Input;
- int ProbeSize;
- int Result;
- int No;
- bool_t PosPrevKey = p->SeekAfterSyncPrevKey;
- fraction Pos = p->SeekAfterSync;
- p->SeekAfterSync.Num = -1;
- p->InSeek = 0;
- p->TemporaryHidden = Pos.Num > 0;
- if (p->CurrentChanged || p->PlayList[p->Current].Changed || p->IOError)
- {
- tchar_t Mime[MAXMIME];
- int Length;
- pin Comment;
- bool_t Network;
- if (p->Format)
- Pos.Num = -1; // invalidate last SeekAfterSync
- Unload(p,1,OnlyLocal,0);
- // find input
- URL = p->PlayList[p->Current].URL;
- if (!URL[0])
- {
- if (!Silent)
- UpdateTimeouts(p);
- return ERR_NONE;
- }
- GetMime(URL,Mime,TSIZEOF(Mime),&p->HasHost);
- Network = p->HasHost || (URL[0]=='\' && URL[1]=='\');
- if (OnlyLocal && Network)
- {
- if (!Silent)
- UpdateTimeouts(p);
- return ERR_FILE_NOT_FOUND;
- }
- Input = GetStream(URL,Silent);
- if (!Input)
- {
- if (!Silent)
- UpdateTimeouts(p);
- return ERR_NOT_SUPPORTED;
- }
- if (p->PlayList[p->Current].Title[0])
- tcscpy_s(p->Title,TSIZEOF(p->Title),p->PlayList[p->Current].Title);
- else
- URLToTitle(p->Title,TSIZEOF(p->Title),URL);
- memset(p->StreamSkip,0,sizeof(p->StreamSkip));
- UpdateSpeed(p);
- SetTimerNode(p,NodeEnumObject(NULL,SYSTIMER_ID),0); // restore system timer
- p->Input = Input;
- Comment.No = PLAYER_COMMENT;
- Comment.Node = (node*)p;
- p->Input->Set(p->Input,STREAM_COMMENT,&Comment,sizeof(pin));
- p->Input->Set(p->Input,STREAM_SILENT,&Silent,sizeof(Silent));
- Result = p->Input->Set(p->Input,STREAM_URL,URL,TSIZEOF(p->PlayList[p->Current].URL));
- if (Result != ERR_NONE)
- {
- Unload(p,Silent,0,1);
- return Result;
- }
- newstream:
- if (p->Input->Get(p->Input,STREAM_LENGTH,&Length,sizeof(int))!=ERR_NONE)
- Length = -1;
- // find format
- if (!AllocBlock(PROBE_SIZE,&Probe,0,HEAP_ANY))
- {
- Unload(p,Silent,0,1);
- return ERR_OUT_OF_MEMORY;
- }
- ProbeSize = p->Input->ReadBlock(p->Input,&Probe,0,PROBE_SIZE);
- // important: get content type after probe was read
- if (p->Input->Get(p->Input,STREAM_CONTENTTYPE,ContentType,sizeof(ContentType))!=ERR_NONE)
- ContentType[0] = 0;
- if (ProbeSize > 0)
- {
- int *i;
- if (!p->InputBase)
- {
- node* Process = NULL;
- NodeEnumClassEx(&List,STREAMPROCESS_CLASS,ContentType,URL,Probe.Ptr,ProbeSize);
- for (i=ARRAYBEGIN(List,int);i!=ARRAYEND(List,int);++i)
- {
- Process = NodeCreate(*i);
- if (Process)
- {
- Process->Set(Process,STREAM_COMMENT,&Comment,sizeof(pin));
- Process->Set(Process,STREAM_SILENT,&Silent,sizeof(Silent));
- if (Process->Set(Process,STREAMPROCESS_INPUT,&p->Input,sizeof(node*)) == ERR_NONE)
- break;
- NodeDelete(Process);
- }
- }
- ArrayClear(&List);
- if (Process)
- {
- p->InputBase = p->Input;
- p->Input = (stream*)Process;
- FreeBlock(&Probe);
- goto newstream;
- }
- }
- NodeEnumClassEx(&List,FORMAT_CLASS,ContentType,URL,Probe.Ptr,ProbeSize);
- for (i=ARRAYBEGIN(List,int);i!=ARRAYEND(List,int);++i)
- {
- p->Format = (format*)NodeCreate(*i);
- if (p->Format)
- {
- notify Notify;
- int Align = p->HasHost?1:ALIGN_SIZE;
- bool_t FindSubtitles = !p->HasHost;
- UpdateFormat(p,0); // set buffersize as soon as possible
- p->Format->Set(p->Format,FORMAT_AUTO_READSIZE,&Network,sizeof(bool_t));
- p->Format->Set(p->Format,FORMAT_FILEALIGN,&Align,sizeof(Align));
- p->Format->Set(p->Format,FORMAT_DATAFEED,Probe.Ptr,ProbeSize);
- p->Format->Set(p->Format,FORMAT_FIND_SUBTITLES,&FindSubtitles,sizeof(FindSubtitles));
- p->Format->Set(p->Format,FORMAT_GLOBAL_COMMENT,&Comment,sizeof(pin));
- Notify.This = p;
- Notify.Func = (notifyfunc)UpdateStreamsNotify;
- p->Format->Set(p->Format,FORMAT_UPDATESTREAMS,&Notify,sizeof(Notify));
- Notify.This = p;
- Notify.Func = (notifyfunc)ReleaseStreamsNotify;
- p->Format->Set(p->Format,FORMAT_RELEASESTREAMS,&Notify,sizeof(Notify));
- if (p->Format->Set(p->Format,FORMAT_INPUT,&p->Input,sizeof(stream*)) == ERR_NONE)
- break;
- ReleaseFormat(p);
- if (p->Input->Seek(p->Input,0,SEEK_CUR) > PROBE_SIZE) //maybe less if file smaller as PROBE_SIZE
- p->Input->Seek(p->Input,PROBE_SIZE,SEEK_SET);
- }
- }
- ArrayClear(&List);
- if (!p->Format && !Nested)
- {
- NodeEnumClassEx(&List,PLAYLIST_CLASS,ContentType,URL,Probe.Ptr,ProbeSize);
- if (!ARRAYEMPTY(List))
- {
- int Index = p->PlayListCount;
- if (LoadPlaylist(&p->Player,&List,p->Input,&Index,URL,Probe.Ptr,ProbeSize))
- {
- FreeBlock(&Probe);
- // remove this from playlist
- for (No=p->Current+1;No<p->PlayListCount;++No)
- ListSwap(p,No-1,No);
- SetPlayListCount(p,p->PlayListCount-1);
- return Load(p,Silent,OnlyLocal,1);
- }
- }
- ArrayClear(&List);
- }
- }
- FreeBlock(&Probe);
- if (!p->Format)
- {
- Unload(p,0,0,1);
- if (ProbeSize < 0)
- ShowError(0,ERR_ID,ERR_FILE_NOT_FOUND,URL);
- else
- if (!CheckExts(URL,T("exe:P")))
- ShowError(PLAYER_ID,PLAYER_ID,PLAYER_FORMAT_NOT_FOUND,URL);
- return ERR_NOT_SUPPORTED;
- }
- if (p->Player.CommentByName(p,-1,PlayerComment(COMMENT_REDIRECT),ContentType,TSIZEOF(ContentType)) &&
- ReDirect(p,ContentType,URL,Silent,OnlyLocal,Nested,&Result))
- return Result;
- // only after format initialized (may have reopened http connection with different pragma)
- p->Streaming = p->HasHost &&
- (p->Input->Get(p->Input,STREAM_LENGTH,&Length,sizeof(int))!=ERR_NONE || Length<0);
- p->Timing = 1;
- p->Format->Get(p->Format,FORMAT_TIMING,&p->Timing,sizeof(p->Timing));
- p->SkipAccelFrom = 0;
- p->PlayList[p->Current].Changed = 0;
- p->CurrentChanged = p->HasHost; // force reload when example connection stops
- p->NoMoreInput = 0;
- p->Position = 0;
- p->BufferMax = p->CurrBufferSize2;
- p->Sync = 1; // start with syncing
- p->PosNotify = -1;
- p->MinBuffer = 0;
- Notify(p,PLAYER_TITLE,1);
- Flush(p);
- if (Pos.Num > 0)
- {
- p->SeekAfterSync = Pos; // restore syncaftersync (now we are sure there will a synced event)
- p->SeekAfterSyncPrevKey = PosPrevKey;
- }
- StopLoadMode(p);
- UpdatePlay(p);
- }
- else
- {
- UpdateFormat(p,0);
- UpdateStreams(p,0,0);
- ClearStats(p);
- if (Pos.Num > 0)
- Result = Seek2(p,-1,&Pos,1);
- else
- Result = Seek2(p,0,NULL,0);
- if (Result != ERR_NONE)
- return Result;
- }
- UpdateRunInput(p);
- UpdateRunProcess(p);
- UpdateTimeouts(p);
- #ifdef MULTITHREAD
- EventSet(p->EventProcess); // wakeup input threads (if needed)
- #else
- p->WaitForProcess = 0;
- #endif
- return ERR_NONE;
- }
- static int InputThread(player_base* p);
- static int ProcessThread(player_base* p);
- static void Unlock(player_base* p, bool_t Input)
- {
- #ifdef MULTITHREAD
- //DEBUG_MSG2(DEBUG_PLAYER,T("Unlocked %d %08x"),p->LockCount,GetCurrentThreadId());
- if (Input)
- {
- LockLeave(p->LockInput);
- --p->LockInputCount;
- assert(p->LockInputCount>=0);
- }
- LockLeave(p->LockProcess);
- --p->LockProcessCount;
- assert(p->LockProcessCount>=0);
- LockLeave(p->Lock);
- #endif
- }
- static void Lock(player_base* p, bool_t Input)
- {
- #ifdef MULTITHREAD
- //DEBUG_MSG2(DEBUG_PLAYER,T("Locked Start %d %08x"),p->LockCount,GetCurrentThreadId());
- assert(p->ProcessLocked != ThreadId() && p->InputLocked != ThreadId());
- LockEnter(p->Lock);
- p->LockProcessCount++;
- LockEnter(p->LockProcess);
- if (Input)
- {
- p->LockInputCount++;
- LockEnter(p->LockInput);
- }
- //DEBUG_MSG(DEBUG_PLAYER,T("Locked End"));
- #endif
- }
- static int UpdateWnd(player_base* p,void* Wnd)
- {
- if (Wnd != p->Wnd)
- {
- p->Wnd = Wnd;
- if (p->Wnd)
- {
- #ifdef MULTITHREAD
- p->InputThread = ThreadCreate(InputThread,p,25);
- p->ProcessThread = ThreadCreate(ProcessThread,p,25);
- p->InputPriority = 0;
- p->ProcessPriority = 0;
- #endif
- Lock(p,1);
- if (p->PlayListCount && !p->DiscardList)
- Load(p,1,1,0);
- else
- {
- SetPlayListCount(p,0);
- p->SeekAfterSync.Num = -1;
- }
- Unlock(p,1);
- }
- else
- {
- Lock(p,1);
- if (!p->KeepList)
- SetPlayListCount(p,0);
- #ifndef REGISTRY_GLOBAL
- NodeRegSave((node*)p);
- NodeRegSave(p->Color);
- NodeRegSave(p->Equalizer);
- #endif
- Unlock(p,1);
- #ifdef MULTITHREAD
- p->RunProcess = 0;
- ThreadPriority(p->ProcessThread,0);
- EventSet(p->EventRunProcess);
- ThreadTerminate(p->ProcessThread);
- p->ProcessThread = NULL;
- p->RunInput = 0;
- ThreadPriority(p->InputThread,0);
- EventSet(p->EventProcess);
- EventSet(p->EventRunInput);
- ThreadTerminate(p->InputThread);
- p->InputThread = NULL;
- #endif
- Unload(p,0,0,0);
- }
- }
- return ERR_NONE;
- }
- static int NextPrev(player_base* p,int Dir,bool_t StopIfEnd,bool_t NotTheSame)
- {
- if (p->PlayListCount > 0)
- {
- int Index = FindCurrentIndex(p);
- int Tries;
- for (Tries=NotTheSame?1:0;Tries<p->PlayListCount;++Tries)
- {
- Index += Dir;
- if (Index >= p->PlayListCount || Index < 0)
- {
- if (StopIfEnd && p->ExitAtEnd)
- Notify(p,PLAYER_EXIT_AT_END,0);
- if (StopIfEnd && !p->Repeat)
- {
- SetPlay(p,1);
- Notify(p,PLAYER_PERCENT,0);
- return ERR_NONE;
- }
- RandomizePlayIndex(p,0);
- Index -= Dir*p->PlayListCount;
- }
- if (p->Current != p->PlayIndex[Index])
- {
- p->Current = p->PlayIndex[Index];
- p->CurrentChanged = 1;
- }
- if (Load(p,1,0,0) == ERR_NONE)
- return ERR_NONE;
- if (!Dir)
- break;
- }
- SetPlay(p,1);
- Notify(p,PLAYER_PERCENT,0);
- }
- else
- Unload(p,0,0,1);
- return ERR_NEED_MORE_DATA;
- }
- static int ProcessThread(player_base* p)
- {
- int Result = ERR_NONE;
- #ifdef MULTITHREAD
- SAFE_BEGIN
- while (p->Wnd)
- {
- LockEnter(p->LockProcess);
- #ifndef NDEBUG
- p->ProcessLocked = ThreadId();
- #endif
- #endif
- if (p->RunProcess)
- {
- processstate State;
- State.Fill = p->Fill;
- p->Timer->Get(p->Timer,TIMER_TIME,&State.Time,sizeof(tick_t));
- //DEBUG_MSG1(DEBUG_PLAYER,T("Process Time:%d"),State.Time);
- Result = p->Format->Process(p->Format,&State);
- if (Result == ERR_SYNCED)
- {
- DEBUG_MSG3(DEBUG_PLAYER,T("Synced: %d (%d,%d)"),State.Time,p->SeekAfterSync.Num,p->SeekAfterSync.Den);
- p->Sync = 0;
- p->Position = State.Time;
- if (State.Time < 0)
- State.Time = 0;
- p->Timer->Set(p->Timer,TIMER_TIME,&State.Time,sizeof(tick_t));
- if (!p->Bench && p->SeekAfterSync.Num<0)
- {
- // enter fill mode (don't stop processing, even if playing if off)
- p->Fill = 1;
- }
- UpdateBuffer(p);
- UpdatePlay(p);
- UpdateRunProcess(p);
- UpdateTimeouts(p); // we are ready to check VOutput and update timeouts
- }
- else
- if (p->Fill && (Result == ERR_END_OF_FILE || Result == ERR_BUFFER_FULL
- || (Result == ERR_NEED_MORE_DATA && (p->NoMoreInput || State.BufferUsedAfter >= p->CurrBufferSize2-2))))
- {
- DEBUG_MSG1(DEBUG_PLAYER,T("Filled: %d"),State.Time);
- if (Result == ERR_END_OF_FILE || Result == ERR_NEED_MORE_DATA)
- Result = ERR_BUFFER_FULL;
- p->Fill = 0;
- UpdatePlay(p);
- UpdateRunProcess(p);
- }
- if (Result == ERR_NEED_MORE_DATA)
- {
- p->BufferMax = p->CurrBufferSize2;
- if (p->NoMoreInput)
- {
- // IOError (Format doesn't know about NoMoreInput)
- Result = ERR_END_OF_FILE;
- }
- #ifdef MULTITHREAD
- else
- if (!p->LoadMode && (p->Play || p->FFwd) && !p->Bench && !p->Sync && !p->Fill)
- {
- if (State.BufferUsedAfter >= (p->CurrBufferSize2>>1))
- {
- if (!p->BufferWarning && State.BufferUsedAfter >= p->CurrBufferSize2-2)
- {
- p->BufferWarning = 1;
- ShowError(PLAYER_ID,PLAYER_ID,PLAYER_BUFFER_WARNING);
- }
- }
- else
- {
- DEBUG_MSG(DEBUG_TEST,T("LoadMode Enter"));
- p->LoadMode = 1;
- p->Fill = 1;
- UpdatePlay(p); // UpdatetPriority(p) called too
- UpdateRunProcess(p);
- UpdateRunInput(p);
- Notify(p,PLAYER_LOADMODE,p->LoadMode);
- }
- }
- #endif
- }
- if (!p->Sync && p->Position>=0)
- p->Position = State.Time;
- #ifdef MULTITHREAD
- LockLeave(p->LockProcess);
- #ifndef NDEBUG
- p->ProcessLocked = 0;
- #endif
- #endif
- if (p->UpdateStreamsNeeded)
- {
- Lock(p,1);
- UpdateStreams(p,-1,1);
- Seek2(p,p->Position>=0?p->Position:0,NULL,0);
- Unlock(p,1);
- Result = ERR_NONE;
- }
- switch (Result)
- {
- case ERR_SYNCED:
- Notify(p,PLAYER_TITLE,0);
- if (p->Current < p->PlayListCount && !p->PlayList[p->Current].Changed &&
- p->PlayList[p->Current].Length<0)
- {
- tick_t Duration;
- Lock(p,0);
- if (p->Format && p->Format->Get(p->Format,FORMAT_DURATION,&Duration,sizeof(Duration))==ERR_NONE)
- UpdateListLength(p,Duration);
- Unlock(p,0);
- }
- if (p->TemporaryHidden)
- {
- Lock(p,0);
- p->TemporaryHidden = 0;
- UpdateVideo(p,p->SeekAfterSync.Num>=0?-1:0);
- Unlock(p,0);
- }
- if (!p->VOutput && p->FullScreen)
- Notify(p,PLAYER_FULLSCREEN,0);
- p->FullScreenAfterSync = 0;
- p->PosNotify = State.Time;
- if (p->SeekAfterSync.Num>=0)
- {
- Lock(p,1);
- if (p->SeekAfterSync.Num>=0) // may have changed
- {
- fraction Pos;
- Pos = p->SeekAfterSync;
- p->SeekAfterSync.Num = -1;
- Seek2(p,-1,&Pos,p->SeekAfterSyncPrevKey);
- }
- Unlock(p,1);
- }
- break;
- case ERR_END_OF_FILE:
- if ((p->Play || p->FFwd || p->Sync) && !p->InSeek)
- {
- Lock(p,1);
- if (p->Bench || p->IOError)
- SetPlay(p,1); // stop benchmarking and show results
- else
- {
- tick_t Duration;
- bool_t NotTheSame = p->Sync ||
- (State.Time < TICKSPERSEC && p->Format &&
- (p->Format->Get(p->Format,FORMAT_DURATION,&Duration,sizeof(Duration))!=ERR_NONE || Duration > TICKSPERSEC*2));
- NextPrev(p,1,1,NotTheSame);
- }
- Unlock(p,1);
- }
- break;
- case ERR_NEED_MORE_DATA:
- //DEBUG_MSG(DEBUG_PLAYER,T("Process Needs More Data Sleep"));
- #ifdef MULTITHREAD
- if (p->InputPriority>0)
- {
- p->InputPriority = 0;
- ThreadPriority(p->InputThread,p->InputPriority);
- }
- p->WaitForProcess = 0;
- EventSet(p->EventProcess); // signal InputThread to coninue
- ThreadSleep(p->Sync?4:25);
- #else
- p->WaitForProcess = 0;
- InputThread(p);
- Result = ERR_NONE; // should not return ERR_BUFFER_FULL
- #endif
- break;
- case ERR_BUFFER_FULL: // no packets processed, we should wait
- //DEBUG_MSG(DEBUG_PLAYER,T("Process Buffer Full Sleep"));
- #ifdef MULTITHREAD
- ThreadSleep(4); // this should be more intelligent...
- #else
- if (!p->WaitForProcess)
- Result = InputThread(p);
- #endif
- break;
- default:
- #ifdef MULTITHREAD
- if (p->WaitForProcess && State.BufferUsedAfter<State.BufferUsedBefore)
- {
- EventSet(p->EventProcess); // signal InputThread to coninue
- if (p->Sync)
- ThreadSleep(5); // invalid media could block everything (going through the file)
- }
- if (p->LockProcessCount > 0 || (!p->Sync && (State.BufferUsedAfter<p->UsedEnough2 || p->Clipping || !p->TimerPlay)))
- {
- DEBUG_MSG(DEBUG_PLAYER,T("Process Sleep"));
- ThreadSleep(0);
- }
- #else
- if (p->WaitForProcess && State.BufferUsedAfter<State.BufferUsedBefore)
- p->WaitForProcess = 0;
- if (!p->WaitForProcess && Result != ERR_DROPPING && !p->Sync && !p->Fill)
- InputThread(p);
- #endif
- break;
- }
- if (State.Time >= 0 && State.Time >= p->PosNotify && !p->Sync && !p->Bench && p->SeekAfterSync.Num<0)
- {
- // should be last, because notify handler could call any player function (load,unload,etc...)
- p->PosNotify = State.Time + p->PosNotifyStep;
- Notify(p,PLAYER_PERCENT,1);
- }
- }
- #ifdef MULTITHREAD
- else
- {
- LockLeave(p->LockProcess);
- #ifndef NDEBUG
- p->ProcessLocked = 0;
- #endif
- EventWait(p->EventRunProcess,-1);
- }
- }
- SAFE_END
- return 0;
- }
- #else
- else
- {
- if (p->RunInput && !p->WaitForProcess)
- Result = InputThread(p); // no decoding process -> still read input in background
- else
- Result = ERR_STOPPED;
- }
- return Result;
- }
- #endif
- static int InputThread(player_base* p)
- {
- int Result = ERR_NONE;
- #ifdef MULTITHREAD
- SAFE_BEGIN
- p->IOError = 0;
- while (p->Wnd)
- {
- LockEnter(p->LockInput);
- #ifndef NDEBUG
- p->InputLocked = ThreadId();
- #endif
- #else
- if (!p->WaitForProcess)
- #endif
- if (p->RunInput)
- {
- int Used;
- Result = p->Format->Read(p->Format,p->BufferMax,&Used);
- if (Result == ERR_DEVICE_ERROR)
- {
- if (++p->IOError >= 20)
- Result = ERR_END_OF_FILE;
- }
- else
- p->IOError = 0;
- if (Result == ERR_END_OF_FILE)
- {
- p->NoMoreInput = 1;
- UpdateRunInput(p);
- }
- //DEBUG_MSG3(-1,T("Input read %d (%d) max:%d"),Used,Result,p->BufferMax);
- #ifdef MULTITHREAD
- LockLeave(p->LockInput);
- #ifndef NDEBUG
- p->InputLocked = 0;
- #endif
- #endif
- switch (Result)
- {
- #ifdef MULTITHREAD
- case ERR_DEVICE_ERROR:
- ThreadSleep(100);
- break;
- #endif
- case ERR_NEED_MORE_DATA:
- ThreadSleep(2);
- break;
- case ERR_BUFFER_FULL:
- if (p->LoadMode)
- {
- Lock(p,0);
- StopLoadMode(p);
- Unlock(p,0);
- }
- if (p->MicroDrive && p->BufferMax==p->CurrBufferSize2)
- {
- // buffer fully loaded, wait until it's almost empty
- p->BufferMax = p->BurstStart;
- //DEBUG_MSG1(-1,T("MicroDrive Full: new max:%d"),p->BufferMax);
- }
- DEBUG_MSG(DEBUG_PLAYER,T("Input waiting for process"));
- p->WaitForProcess = 1;
- #ifdef MULTITHREAD
- EventWait(p->EventProcess,p->RunProcess?2000:-1);
- p->WaitForProcess = 0;
- DEBUG_MSG(DEBUG_PLAYER,T("Input waiting over"));
- #endif
- break;
- case ERR_END_OF_FILE:
- if (p->LoadMode)
- {
- Lock(p,0);
- StopLoadMode(p);
- Unlock(p,0);
- }
- Result = ERR_NONE;
- //no break;
- default:
- if (p->BufferMax != p->CurrBufferSize2)
- {
- // microdrive started loading, change back to full buffer
- //DEBUG_MSG(-1,T("MicroDrive needs data"));
- p->BufferMax = p->CurrBufferSize2;
- }
- if (p->LoadMode && !p->Fill && Used >= (p->VOutput ? (p->Streaming ? VIDEO_STREAMING_UNDERRUN:((p->CurrBufferSize2 * p->UnderRun)/PERCENT_ONE)) : ((p->AudioUnderRun*1024)/BLOCKSIZE)))
- {
- Lock(p,0);
- StopLoadMode(p);
- Unlock(p,0);
- }
- #ifdef MULTITHREAD
- if (p->MicroDrive && Used >= (MINBUFFER/BLOCKSIZE))
- ThreadSleep(2); // try to give some time to ProcessThread during read bursts
- else
- if (p->LockInputCount > 0 || (Used>=p->UsedEnough2+p->MinBuffer*2))
- ThreadSleep(0);
- #endif
- break;
- }
- }
- #ifdef MULTITHREAD
- else
- {
- LockLeave(p->LockInput);
- #ifndef NDEBUG
- p->InputLocked = 0;
- #endif
- EventWait(p->EventRunInput,-1);
- }
- }
- SAFE_END
- return 0;
- }
- #else
- return Result;
- }
- #endif
- static void AddComment(player_base* p,int Stream,const tchar_t* Comment)
- {
- const tchar_t* s = tcschr(Comment,'=');
- if (s)
- {
- uint8_t *i;
- tchar_t Null = 0;
- size_t Name = s-Comment;
- ++s;
- LockEnter(p->LockComment);
- for (i=ARRAYBEGIN(p->Comment,uint8_t);i!=ARRAYEND(p->Comment,uint8_t);i=(uint8_t*)CommentNext(i))
- if (CommentStream(i) == Stream && Name==(int)tcslen(CommentName(i)) &&
- tcsnicmp(CommentName(i),Comment,Name)==0)
- {
- const uint8_t* Next = CommentNext(i);
- if (Next == ARRAYEND(p->Comment,uint8_t))
- {
- ArrayAppend(&p->Comment,NULL,i-Next,512); //shrink
- break;
- }
- *(tchar_t*)i = (tchar_t)INVALID_COMMENT; //invalidate old comment
- }
- ArrayAppend(&p->Comment,&Stream,sizeof(tchar_t),512);
- ArrayAppend(&p->Comment,Comment,sizeof(tchar_t)*Name,512);
- ArrayAppend(&p->Comment,&Null,sizeof(tchar_t),512);
- ArrayAppend(&p->Comment,s,sizeof(tchar_t)*(tcslen(s)+1),512);
- LockLeave(p->LockComment);
- if (tcsnicmp(Comment,PlayerComment(COMMENT_TITLE),Name)==0 ||
- tcsnicmp(Comment,PlayerComment(COMMENT_ARTIST),Name)==0 ||
- tcsnicmp(Comment,PlayerComment(COMMENT_AUTHOR),Name)==0)
- UpdateListTitle(p);
- }
- }
- static tick_t GetPosition(player_base* p,tick_t* OutDuration)
- {
- tick_t Duration = 1;
- tick_t Time = p->Timing?p->Position:-1;
- if (p->Format && p->Format->Get(p->Format,FORMAT_DURATION,&Duration,sizeof(Duration))==ERR_NONE && Duration>=0 && Time>=0)
- {
- if (p->InSeek && p->InSeekPos.Num>=0)
- Time = Scale(Duration,p->InSeekPos.Num,p->InSeekPos.Den);
- else
- if (p->SeekAfterSync.Num>=0)
- Time = Scale(Duration,p->SeekAfterSync.Num,p->SeekAfterSync.Den);
- }
- if (OutDuration)
- *OutDuration = Duration;
- return Time;
- }
- static int GetTimerString(player_base* p,tchar_t* Out,int OutLen)
- {
- tick_t Duration;
- tick_t Time = GetPosition(p,&Duration);
- if (Time<0)
- Out[0] = 0;
- else
- {
- if (p->TimerLeft)
- {
- Time = Time - Duration;
- if (Time >= 0)
- Time = -1;
- }
- TickToString(Out,OutLen,Time,0,0,0);
- }
- return ERR_NONE;
- }
- static int UpdateAllVideoQuality(player_base* p)
- {
- const ref *i;
- for (i=ARRAYBEGIN(p->Ref,ref);i!=ARRAYEND(p->Ref,ref);++i)
- UpdateVideoQuality(p,i->Node);
- return ERR_NONE;
- }
- static int Get(player_base* p, int No, void* Data, int Size)
- {
- int Result = ERR_INVALID_PARAM;
- #ifdef MULTITHREAD
- assert(p->ProcessLocked != ThreadId() && p->InputLocked != ThreadId());
- #endif
- LockEnter(p->Lock);
- if (No >= PLAYER_ARRAY)
- {
- if (No >= PLAYER_LIST_URL && No < PLAYER_LIST_URL+p->PlayListCount)
- {
- No -= PLAYER_LIST_URL;
- tcscpy_s((tchar_t*)Data,Size/sizeof(tchar_t),p->PlayList[No].URL);
- Result = ERR_NONE;
- }
- else
- if (No >= PLAYER_LIST_TITLE && No < PLAYER_LIST_TITLE+p->PlayListCount)
- {
- No -= PLAYER_LIST_TITLE;
- tcscpy_s((tchar_t*)Data,Size/sizeof(tchar_t),p->PlayList[No].Title);
- Result = ERR_NONE;
- }
- else
- if (No >= PLAYER_LIST_AUTOTITLE && No < PLAYER_LIST_AUTOTITLE+p->PlayListCount)
- {
- No -= PLAYER_LIST_AUTOTITLE;
- if (p->PlayList[No].Title[0])
- tcscpy_s((tchar_t*)Data,Size/sizeof(tchar_t),p->PlayList[No].Title);
- else
- URLToTitle((tchar_t*)Data,Size/sizeof(tchar_t),p->PlayList[No].URL);
- Result = ERR_NONE;
- }
- else
- if (No >= PLAYER_LIST_LENGTH && No < PLAYER_LIST_LENGTH+p->PlayListCount)
- {
- No -= PLAYER_LIST_LENGTH;
- *(tick_t*)Data = p->PlayList[No].Length;
- Result = ERR_NONE;
- }
- }
- else
- switch (No)
- {
- case PLAYER_VIDEO_OVERLAY: GETVALUE(p->Overlay,bool_t); break;
- case PLAYER_BUFFER_SIZE: GETVALUE((p->BufferSize2*BLOCKSIZE)/1024,int); break;
- case PLAYER_MD_BUFFER_SIZE: GETVALUE((p->MDBufferSize2*BLOCKSIZE)/1024,int); break;
- case PLAYER_BURSTSTART: GETVALUE((p->BurstStart*BLOCKSIZE)/1024,int); break;
- case PLAYER_MICRODRIVE: GETVALUE(p->MicroDrive,bool_t); break;
- case PLAYER_UNDERRUN: GETVALUE(p->UnderRun,int); break;
- case PLAYER_AUDIO_UNDERRUN: GETVALUE(p->AudioUnderRun,int); break;
- case PLAYER_REPEAT: GETVALUE(p->Repeat,bool_t); break;
- case PLAYER_SHUFFLE: GETVALUE(p->Shuffle,bool_t); break;
- case PLAYER_PLAYATOPEN: GETVALUE(p->PlayAtOpen,bool_t); break;
- case PLAYER_PLAYATOPEN_FULL: GETVALUE(p->PlayAtOpenFull,bool_t); break;
- case PLAYER_EXIT_AT_END: GETVALUE(p->ExitAtEnd,bool_t); break;
- case PLAYER_KEEPPLAY_VIDEO: GETVALUE(p->KeepPlayVideo,bool_t); break;
- case PLAYER_SHOWINBACKGROUND: GETVALUE(p->ShowInBackground,bool_t); break;
- case PLAYER_SINGLECLICKFULLSCREEN: GETVALUE(p->SingleClickFullScreen,bool_t); break;
- case PLAYER_KEEPPLAY_AUDIO: GETVALUE(p->KeepPlayAudio,bool_t); break;
- case PLAYER_KEEPLIST: GETVALUE(p->KeepList,bool_t); break;
- case PLAYER_MOVEBACK_STEP: GETVALUE(p->MoveBack,tick_t); break;
- case PLAYER_MOVEFFWD_STEP: GETVALUE(p->MoveFFwd,tick_t); break;
- case PLAYER_PLAY: GETVALUE(p->Play,bool_t); break;
- case PLAYER_FFWD: GETVALUE(p->FFwd,bool_t); break;
- case PLAYER_PLAY_SPEED: GETVALUE(p->PlaySpeed,fraction); break;
- case PLAYER_FFWD_SPEED: GETVALUE(p->FFwdSpeed,fraction); break;
- case PLAYER_TITLE: GETSTRING(p->Title); break;
- case PLAYER_INPUT: GETVALUE(p->Input,stream*); break;
- case PLAYER_FORMAT: GETVALUE(p->Format,format*); break;
- case PLAYER_AOUTPUTID: GETVALUE(p->AOutputId,int); break;
- case PLAYER_VOUTPUTID: GETVALUE(p->VOutputId,int); break;
- case PLAYER_AOUTPUTID_MAX: GETVALUE(p->AOutputIdMax,int); break;
- case PLAYER_VOUTPUTID_MAX: GETVALUE(p->VOutputIdMax,int); break;
- case PLAYER_AOUTPUT: GETVALUE(p->AOutput,node*); break;
- case PLAYER_VOUTPUT: GETVALUE(p->VOutput,node*); break;
- case PLAYER_LIST_COUNT: GETVALUE(p->PlayListCount,int); break;
- case PLAYER_LIST_CURRENT: GETVALUE(p->Current,int); break;
- case PLAYER_LIST_CURRIDX: GETVALUE(FindCurrentIndex(p),int); break;
- case PLAYER_VIDEO_ACCEL: GETVALUE(p->VideoAccel,bool_t); break;
- case PLAYER_AUDIO_QUALITY: GETVALUE(p->AudioQuality,int); break;
- case PLAYER_FULLSCREEN: GETVALUE(p->FullScreen,bool_t); break;
- case PLAYER_AUTOPREROTATE: GETVALUE(p->AutoPreRotate,bool_t); break;
- case PLAYER_SKIN_VIEWPORT: GETVALUE(p->SkinViewport,rect); break;
- case PLAYER_CLIPPING: GETVALUE(p->Clipping,bool_t); break;
- case PLAYER_ASPECT: GETVALUE(p->Aspect,fraction); break;
- case PLAYER_STEREO: GETVALUE(p->Stereo,int); break;
- case PLAYER_FULL_ZOOM: GETVALUE(p->FullZoom,fraction); break;
- case PLAYER_SKIN_ZOOM: GETVALUE(p->SkinZoom,fraction); break;
- case PLAYER_SMOOTH50: GETVALUE(p->SmoothZoom50,bool_t); break;
- case PLAYER_SMOOTHALWAYS: GETVALUE(p->SmoothZoomAlways,bool_t); break;
- case PLAYER_FULL_DIR: GETVALUE(p->FullDir,int); break;
- case PLAYER_SKIN_DIR: GETVALUE(p->SkinDir,int); break;
- case PLAYER_REL_DIR: GETVALUE(p->RelDir,int); break;
- case PLAYER_BENCHMARK_SRC: GETVALUE(p->BenchSrc,point); break;
- case PLAYER_BENCHMARK_DST: GETVALUE(p->BenchDst,point); break;
- case PLAYER_BENCHMARK: GETVALUECOND(p->BenchTime,tick_t,!p->Bench); break;
- case PLAYER_POSITION: GETVALUECOND(GetPosition(p,NULL),tick_t,p->Position>=0); break;
- case PLAYER_LOADMODE: GETVALUE(p->LoadMode,bool_t); break;
- case PLAYER_NOTIFY: GETVALUE(p->Notify,notify); break;
- case PLAYER_LIST_NOTIFY: GETVALUE(p->ListNotify,notify); break;
- case PLAYER_VSTREAM: GETVALUE(p->Selected[PACKET_VIDEO],int); break;
- case PLAYER_ASTREAM: GETVALUE(p->Selected[PACKET_AUDIO],int); break;
- case PLAYER_SUBSTREAM: GETVALUE(p->Selected[PACKET_SUBTITLE],int); break;
- case PLAYER_SYNCING: GETVALUE(p->Sync || !p->Format,bool_t); break;
- case PLAYER_BACKGROUND: GETVALUE(p->Background,bool_t); break;
- case PLAYER_FOREGROUND: GETVALUE(p->Foreground,bool_t); break;
- case PLAYER_STREAMING: GETVALUE(p->Streaming,bool_t) break;
- case PLAYER_INSEEK: GETVALUE(p->InSeek,bool_t); break;
- case PLAYER_DISCARDLIST: GETVALUE(p->DiscardList,bool_t); break;
- case PLAYER_VIDEO_QUALITY: GETVALUE(p->VideoQuality,int); if (!SupportVQ(p)) Result = ERR_NOT_SUPPORTED; break;
- case PLAYER_PERCENT:
- if (p->Format && p->Input)
- {
- fraction* Percent = (fraction*)Data;
- tick_t Duration;
- int FilePos;
- int FileSize;
- tick_t Position = p->Position;
- if (p->InSeek && p->InSeekPos.Num>=0)
- {
- Percent->Num = p->InSeekPos.Num;
- Percent->Den = p->InSeekPos.Den;
- Result = ERR_NONE;
- }
- else
- if (p->SeekAfterSync.Num>=0)
- {
- Percent->Num = p->SeekAfterSync.Num;
- Percent->Den = p->SeekAfterSync.Den;
- Result = ERR_NONE;
- }
- else
- if (Position >= 0 && p->Format->Get(p->Format,FORMAT_DURATION,&Duration,sizeof(Duration))==ERR_NONE)
- {
- UpdateListLength(p,Duration);
- Percent->Num = Position;
- Percent->Den = Duration;
- Result = ERR_NONE;
- }
- else
- if (Position==0 && !p->Streaming && p->Timing)
- {
- Percent->Num = 0;
- Percent->Den = 0;
- Result = ERR_NONE;
- }
- else
- if (p->Input->Get(p->Input,STREAM_LENGTH,&FileSize,sizeof(FileSize))==ERR_NONE &&
- p->Format->Get(p->Format,FORMAT_FILEPOS,&FilePos,sizeof(FilePos))==ERR_NONE)
- {
- Percent->Num = FilePos;
- Percent->Den = FileSize;
- Result = ERR_NONE;
- }
- }
- break;
- case PLAYER_CURRENTDIR: GETSTRING(p->CurrentDir); break;
- case PLAYER_MUTE: GETVALUE(RefreshAudio(p)->Mute,bool_t); break;
- case PLAYER_VOLUME: GETVALUE(RefreshAudio(p)->Volume,int); break;
- case PLAYER_PAN: GETVALUE(RefreshAudio(p)->Pan,int); break;
- case PLAYER_PREAMP: GETVALUE(p->PreAmp,int); break;
- case PLAYER_TIMER_LEFT: GETVALUE(p->TimerLeft,bool_t); break;
- case PLAYER_TIMER: Result = GetTimerString(p,Data,Size/sizeof(tchar_t)); break;
- case PLAYER_DURATION:
- if (p->Format)
- {
- Result = p->Format->Get(p->Format,FORMAT_DURATION,Data,Size);
- if (Result == ERR_NONE)
- UpdateListLength(p,*(tick_t*)Data);
- }
- break;
- }
- LockLeave(p->Lock);
- return Result;
- }
- static int UpdateInSeek(player_base* p)
- {
- if (p->Format && p->Input && p->Timer)
- {
- fraction Pos = p->SeekAfterSync;
- p->InSeekPos.Num = -1;
- p->SeekAfterSync.Num = -1;
- if (!p->InSeek)
- {
- if (Pos.Num>=0)
- {
- Lock(p,1);
- Seek2(p,-1,&Pos,p->SeekAfterSyncPrevKey);
- Unlock(p,1);
- }
- else
- Notify(p,PLAYER_PERCENT,0);
- }
- UpdatePlay(p);
- }
- return ERR_NONE;
- }
- static int AddCodecSkip(player_base* p,const pin* Pin)
- {
- packetformat Format;
- if (Pin->Node)
- {
- if (p->CodecSkipCount < MAXSKIP &&
- Pin->Node->Get(Pin->Node,Pin->No|PIN_FORMAT,&Format,sizeof(Format))==ERR_NONE)
- {
- array List;
- const int *i;
- PacketFormatEnumClass(&List,&Format);
- for (i=ARRAYBEGIN(List,int);i!=ARRAYEND(List,int);++i)
- if (!IsCodecSkip(p,*i) && *i != Pin->Node->Class)
- {
- // found an alternative
- ArrayClear(&List);
- p->CodecSkip[p->CodecSkipCount++] = Pin->Node->Class;
- p->UpdateStreamsNeeded = 1;
- return ERR_NONE;
- }
- ArrayClear(&List);
- }
- else
- {
- datadef DataDef;
- if (NodeDataDef(Pin->Node,Pin->No,&DataDef) && DataDef.Type==TYPE_NODE && DataDef.Format1==IDCT_CLASS &&
- p->VideoAccel && p->SkipAccelFrom != Pin->Node->Class)
- {
- p->SkipAccelFrom = Pin->Node->Class;
- p->UpdateStreamsNeeded = 1;
- return ERR_NONE;
- }
- }
- }
- return ERR_NOT_SUPPORTED;
- }
- static int SetCurrent(player_base* p,int i,bool_t Index)
- {
- if (i>=p->PlayListCount)
- return ERR_INVALID_PARAM;
- if (i<0)
- Unload(p,0,0,1);
- else
- {
- if (p->Current != i)
- {
- p->Current = i;
- p->CurrentChanged = 1;
- if (Index)
- RandomizePlayIndex(p,1);
- }
- if (p->Wnd)
- {
- int Result = Load(p,0,0,0);
- if (Result == ERR_FILE_NOT_FOUND)
- Result = NextPrev(p,1,1,1);
- if (Result==ERR_NONE && p->Format && (p->Streaming || p->PlayAtOpen || p->PlayAtOpenFull))
- {
- p->FullScreenAfterSync = p->PlayAtOpenFull;
- if (p->FullScreenAfterSync && IsVideo(p) && !p->FullScreen)
- Notify(p,PLAYER_FULLSCREEN,1);
- p->Play = 1;
- p->FFwd = 0;
- SetPlay(p,0);
- }
- }
- }
- return ERR_NONE;
- }
- static NOINLINE bool_t CloseTo(tick_t Prev,tick_t Chapter,tick_t Pos)
- {
- tick_t Limit;
- if (Prev<0) return 0;
- Limit = abs(Chapter-Prev)/32;
- if (Limit>10*TICKSPERSEC)
- Limit=10*TICKSPERSEC;
- return abs(Pos-Chapter)<Limit;
- }
- static NOINLINE tick_t FindChapter(player_base* p,int Dir)
- {
- int No;
- if (p->Position>=0)
- {
- if (Dir<0)
- for (No=MAXCHAPTER-1;No>=0;--No)
- {
- tick_t Prev = PlayerGetChapter(&p->Player,No-1,NULL,0);
- tick_t Chapter = PlayerGetChapter(&p->Player,No,NULL,0);
- if (Chapter >= 0 && Chapter < p->Position && !CloseTo(Prev,Chapter,p->Position))
- return Chapter;
- }
- else
- {
- tick_t Prev = -1;
- for (No=1;No<MAXCHAPTER;++No)
- {
- tick_t Chapter = PlayerGetChapter(&p->Player,No,NULL,0);
- if (Chapter >= 0 && Chapter > p->Position && !CloseTo(Prev,Chapter,p->Position))
- return Chapter;
- Prev = Chapter;
- }
- }
- }
- return -1;
- }
- static int UpdateShowInBackground(player_base* p)
- {
- UpdateBackground(p);
- return UpdateVideo(p,0);
- }
- static void UpdateSoftVolume(player_base* p)
- {
- bool_t SoftVolume = QueryAdvanced(ADVANCED_SYSTEMVOLUME);
- if (p->SoftVolume != SoftVolume)
- {
- p->SoftVolume = SoftVolume;
- Lock(p,0);
- UpdateAudio(p);
- Unlock(p,0);
- }
- }
- static int Set(player_base* p, int No, const void* Data, int Size)
- {
- tick_t t;
- int New,Old;
- int Result = ERR_INVALID_PARAM;
- if (No >= PLAYER_COMMENT && No < PLAYER_COMMENT+MAXSTREAM)
- {
- AddComment(p,No-PLAYER_COMMENT,(const tchar_t*)Data);
- return ERR_NONE;
- }
- switch (No)
- {
- case NODE_HIBERNATE:
- return Result;
- case NODE_CRASH:
- #ifdef MULTITHREAD
- p->RunProcess = 0;
- p->RunInput = 0;
- p->Lock = NULL;
- p->LockInput = NULL;
- p->LockProcess = NULL;
- p->EventProcess = NULL;
- p->EventRunInput = NULL;
- p->EventRunProcess = NULL;
- #ifndef NDEBUG
- p->ProcessLocked = 0;
- p->InputLocked = 0;
- #endif
- #endif
- return ERR_NONE;
- case PLAYER_NOT_SUPPORTED_DATA:
- // called by process thread
- assert(Size == sizeof(pin));
- return AddCodecSkip(p,(const pin*)Data);
- case NODE_SETTINGSCHANGED:
- UpdateWnd(p,Context()->Wnd);
- UpdatePriority(p);
- UpdateEqualizer(p);
- UpdateSoftVolume(p);
- return ERR_NONE;
- case PLAYER_PERCENT:
- assert(Size == sizeof(fraction));
- return InSeek(p,-1,(const fraction*)Data,1);
- case PLAYER_POSITION:
- assert(Size == sizeof(tick_t));
- t = *(const tick_t*)Data;
- if (t<0) t=0;
- return InSeek(p,t,NULL,1);
- case PLAYER_BACKGROUND: SETVALUECMP(p->Background,bool_t,UpdateBackground(p),EqBool); return Result;
- case PLAYER_POWEROFF: SETVALUECMP(p->PowerOff,bool_t,UpdatePowerOff(p),EqBool); return Result;
- }
- Lock(p,0);
- if (No >= PLAYER_LIST_URL && No <= PLAYER_LIST_URL+p->PlayListCount)
- {
- No -= PLAYER_LIST_URL;
- if (No == p->PlayListCount)
- SetPlayListCount(p,No+1);
- if (No < p->PlayListCount)
- {
- if (!Size) Data = T("");
- p->PlayList[No].Changed = 1;
- tcscpy_s(p->PlayList[No].URL,TSIZEOF(p->PlayList[No].URL),(tchar_t*)Data);
- NotifyList(p);
- Result = ERR_NONE;
- }
- }
- else
- if (No >= PLAYER_LIST_TITLE && No <= PLAYER_LIST_TITLE+p->PlayListCount)
- {
- No -= PLAYER_LIST_TITLE;
- if (No == p->PlayListCount)
- SetPlayListCount(p,No+1);
- if (No < p->PlayListCount)
- {
- if (!Size) Data = T("");
- tcscpy_s(p->PlayList[No].Title,TSIZEOF(p->PlayList[No].Title),(tchar_t*)Data);
- NotifyList(p);
- Result = ERR_NONE;
- }
- }
- else
- if (No >= PLAYER_LIST_LENGTH && No <= PLAYER_LIST_LENGTH+p->PlayListCount)
- {
- No -= PLAYER_LIST_LENGTH;
- if (No == p->PlayListCount)
- SetPlayListCount(p,No+1);
- if (No < p->PlayListCount)
- {
- p->PlayList[No].Length = *(tick_t*)Data;
- NotifyList(p);
- Result = ERR_NONE;
- }
- }
- else
- switch (No)
- {
- case PLAYER_TIMER_LEFT: SETVALUE(p->TimerLeft,bool_t,ERR_NONE); break;
- case PLAYER_PLAY_SPEED: SETVALUE(p->PlaySpeed,fraction,UpdateSpeed(p)); break;
- case PLAYER_FFWD_SPEED: SETVALUE(p->FFwdSpeed,fraction,UpdateSpeed(p)); break;
- case PLAYER_REPEAT: SETVALUE(p->Repeat,bool_t,ERR_NONE); break;
- case PLAYER_PLAYATOPEN: SETVALUE(p->PlayAtOpen,bool_t,ERR_NONE); break;
- case PLAYER_PLAYATOPEN_FULL: SETVALUE(p->PlayAtOpenFull,bool_t,ERR_NONE); break;
- case PLAYER_EXIT_AT_END: SETVALUE(p->ExitAtEnd,bool_t,ERR_NONE); break;
- case PLAYER_KEEPPLAY_AUDIO: SETVALUE(p->KeepPlayAudio,bool_t,ERR_NONE); break;
- case PLAYER_KEEPPLAY_VIDEO: SETVALUE(p->KeepPlayVideo,bool_t,ERR_NONE); break;
- case PLAYER_SHOWINBACKGROUND: SETVALUE(p->ShowInBackground,bool_t,UpdateShowInBackground(p)); break;
- case PLAYER_SINGLECLICKFULLSCREEN: SETVALUE(p->SingleClickFullScreen,bool_t,ERR_NONE); break;
- case PLAYER_KEEPLIST: SETVALUE(p->KeepList,bool_t,ERR_NONE); break;
- case PLAYER_MOVEBACK_STEP: SETVALUE(p->MoveBack,tick_t,ERR_NONE); break;
- case PLAYER_MOVEFFWD_STEP: SETVALUE(p->MoveFFwd,tick_t,ERR_NONE); break;
- case PLAYER_SHUFFLE: SETVALUE(p->Shuffle,bool_t,RandomizePlayIndex(p,1)); break;
- case PLAYER_AUDIO_QUALITY: SETVALUECMP(p->AudioQuality,int,UpdateAudio(p),EqInt); break;
- case PLAYER_VIDEO_QUALITY: SETVALUE(p->VideoQuality,int,UpdateAllVideoQuality(p)); break;
- case PLAYER_FULLSCREEN: SETVALUECMP(p->FullScreen,bool_t,ERR_NONE,EqBool); break;
- case PLAYER_AUTOPREROTATE: SETVALUECMP(p->AutoPreRotate,bool_t,UpdateVideo(p,0),EqBool); break;
- case PLAYER_SKIN_VIEWPORT: SETVALUECMP(p->SkinViewport,rect,ERR_NONE,EqRect); break;
- case PLAYER_CLIPPING: SETVALUECMP(p->Clipping,bool_t,UpdateVideo(p,0),EqBool); break;
- case PLAYER_ASPECT: SETVALUECMP(p->Aspect,fraction,UpdateVideo(p,0),EqFrac); break;
- case PLAYER_STEREO: SETVALUECMP(p->Stereo,int,UpdateAudio(p),EqInt); break;
- case PLAYER_FULL_ZOOM: SETVALUECMP(p->FullZoom,fraction,UpdateVideo(p,0),EqFrac); break;
- case PLAYER_SKIN_ZOOM: SETVALUECMP(p->SkinZoom,fraction,UpdateVideo(p,0),EqFrac); break;
- case PLAYER_SMOOTH50: SETVALUECMP(p->SmoothZoom50,bool_t,UpdateVideo(p,0),EqBool); break;
- case PLAYER_SMOOTHALWAYS: SETVALUECMP(p->SmoothZoomAlways,bool_t,UpdateVideo(p,0),EqBool); break;
- case PLAYER_FULL_DIR: SETVALUECMP(p->FullDir,int,UpdateVideo(p,0),EqInt); break;
- case PLAYER_SKIN_DIR: SETVALUECMP(p->SkinDir,int,UpdateVideo(p,0),EqInt); break;
- case PLAYER_VIDEO_ACCEL: SETVALUE(p->VideoAccel,bool_t,ERR_NONE); break;
- case PLAYER_VSTREAM: SETVALUECMP(p->Selected[PACKET_VIDEO],int,UpdateStreamsMask(p),EqInt); break;
- case PLAYER_ASTREAM: SETVALUECMP(p->Selected[PACKET_AUDIO],int,UpdateStreamsMask(p),EqInt); break;
- case PLAYER_SUBSTREAM: SETVALUECMP(p->Selected[PACKET_SUBTITLE],int,UpdateStreamsMask(p),EqInt); break;
- case PLAYER_NOTIFY: SETVALUENULL(p->Notify,notify,ERR_NONE,p->Notify.Func=NULL); break;
- case PLAYER_LIST_NOTIFY: SETVALUENULL(p->ListNotify,notify,ERR_NONE,p->ListNotify.Func=NULL); break;
- case PLAYER_FOREGROUND: SETVALUECMP(p->Foreground,bool_t,UpdateForeground(p),EqBool); break;
- case PLAYER_INSEEK: SETVALUECMP(p->InSeek,bool_t,UpdateInSeek(p),EqBool); break;
- case PLAYER_MUTE: SETVALUECMP(p->Mute,bool_t,UpdateAudio(p),EqBool); break;
- case PLAYER_CURRENTDIR: SETSTRING(p->CurrentDir); break;
- case PLAYER_DISCARDLIST: SETVALUE(p->DiscardList,bool_t,ERR_NONE); break;
- case PLAYER_PREAMP: SETVALUECMP(p->PreAmp,int,UpdateAudio(p),EqInt); break;
- case PLAYER_PAN: SETVALUECMP(p->Pan,int,UpdateAudio(p),EqInt); break;
- case PLAYER_VOLUME: SETVALUECMP(p->Volume,int,UpdateVolume(p),EqInt); break; // need process locking for software win32 waveout pausing
- case PLAYER_UPDATEVIDEO:
- Result = UpdateVideo(p,0);
- break;
- case PLAYER_ROTATEEND:
- p->Rotating = 0;
- Result = UpdateVideo(p,0);
- break;
- case PLAYER_RESETVIDEO:
- if (p->VOutput)
- p->VOutput->Set(p->VOutput,VOUT_RESET,NULL,0);
- break;
- case PLAYER_ROTATEBEGIN:
- p->Rotating = 1;
- if (p->VOutput)
- {
- bool_t Visible = 0;
- p->VOutput->Set(p->VOutput,VOUT_VISIBLE,&Visible,sizeof(Visible));
- }
- break;
- case PLAYER_VOUTPUTID:
- assert(Size==sizeof(int));
- Old = p->VOutputId;
- New = *(int*)Data;
- if (New && !NodeEnumClass(NULL,New)) // old driver removed
- {
- New = p->VOutputIdMax;
- p->VideoAccel = VOutIDCT(New);
- }
- p->VOutputId = New;
- p->SkipAccelFrom = 0;
- if (!VOutIDCT(New))
- p->VideoAccel = 0;
- UpdateStreams(p,Old==0,0);
- break;
- case PLAYER_AOUTPUTID:
- assert(Size==sizeof(int));
- Old = p->AOutputId;
- New = *(int*)Data;
- if (New && !NodeEnumClass(NULL,New)) // old driver removed
- New = p->AOutputIdMax;
- p->AOutputId = New;
- UpdateStreams(p,Old==0,0);
- break;
- case PLAYER_UPDATEEQUALIZER:
- Result = UpdateEqualizer(p);
- break;
- case PLAYER_VOUTPUTID_MAX:
- assert(Size==sizeof(int));
- if (p->VOutputIdMax != *(int*)Data && p->VOutputId && p->VOutputIdMax && p->VOutputId != p->VOutputIdMax)
- {
- // new highest priority video driver added. use it
- p->VOutputId = p->VOutputIdMax;
- p->VideoAccel = VOutIDCT(p->VOutputId);
- UpdateStreams(p,0,0);
- }
- Result = ERR_NONE;
- break;
- case PLAYER_AOUTPUTID_MAX:
- assert(Size==sizeof(int));
- if (p->AOutputIdMax != *(int*)Data && p->AOutputId && p->AOutputIdMax && p->AOutputId != p->AOutputIdMax)
- {
- // new highest priority audio driver added. use it
- p->AOutputId = p->AOutputIdMax;
- UpdateStreams(p,0,0);
- }
- Result = ERR_NONE;
- break;
- case PLAYER_MOVEBACK:
- case PLAYER_MOVEFFWD:
- if (p->Format && p->Input)
- {
- int FilePos;
- int FileSize;
- tick_t Position = GetPosition(p,NULL);
- fraction Percent;
- if (Position >= 0)
- {
- if (No == PLAYER_MOVEBACK)
- Position -= p->MoveBack;
- else
- Position += p->MoveFFwd;
- if (Position<0)
- Position=0;
- Result = InSeek(p,Position,NULL,No == PLAYER_MOVEBACK);
- }
- else
- if (p->Input->Get(p->Input,STREAM_LENGTH,&FileSize,sizeof(FileSize))==ERR_NONE &&
- p->Format->Get(p->Format,FORMAT_FILEPOS,&FilePos,sizeof(FilePos))==ERR_NONE)
- {
- if (No == PLAYER_MOVEBACK)
- FilePos -= FileSize/256;
- else
- FilePos += FileSize/256;
- Percent.Num = FilePos;
- Percent.Den = FileSize;
- if (Percent.Num<0)
- Percent.Num=0;
- Result = InSeek(p,-1,&Percent,No == PLAYER_MOVEBACK);
- }
- else
- Result = p->Player.Set(&p->Player,No == PLAYER_MOVEBACK?PLAYER_PREV:PLAYER_NEXT,NULL,0);
- }
- break;
- default:
- // inputthread locking needed for these
- Lock(p,1);
- switch (No)
- {
- case PLAYER_BUFFER_SIZE:
- assert(Size==sizeof(int));
- p->BufferSize2 = (*(int*)Data *1024)/BLOCKSIZE;
- if (p->BufferSize2 < MINBUFFER/BLOCKSIZE)
- p->BufferSize2 = MINBUFFER/BLOCKSIZE;
- Result = UpdateFormat(p,1);
- break;
- case PLAYER_MD_BUFFER_SIZE:
- assert(Size==sizeof(int));
- p->MDBufferSize2 = (*(int*)Data *1024)/BLOCKSIZE;
- if (p->MDBufferSize2 < MINBUFFER/BLOCKSIZE)
- p->MDBufferSize2 = MINBUFFER/BLOCKSIZE;
- Result = UpdateFormat(p,1);
- break;
- case PLAYER_BURSTSTART:
- assert(Size==sizeof(int));
- p->BurstStart = (*(int*)Data *1024)/BLOCKSIZE;
- if (p->BurstStart < MINBUFFER/BLOCKSIZE)
- p->BurstStart = MINBUFFER/BLOCKSIZE;
- Result = ERR_NONE;
- break;
- case PLAYER_BENCHMARK:
- if (p->Format && p->Input)
- StartBench(p);
- break;
- case PLAYER_PLAY:
- if (Size == sizeof(bool_t))
- {
- if (*(bool_t*)Data)
- {
- if (!p->Format && p->Wnd && p->PlayListCount)
- {
- if (Load(p,0,0,0) == ERR_FILE_NOT_FOUND)
- NextPrev(p,1,1,1);
- }
- if (!p->Format)
- {
- Result = ERR_INVALID_DATA;
- break;
- }
- p->Play = 1;
- p->FFwd = 0;
- }
- else
- {
- p->Play = 0;
- p->FFwd = 0;
- p->Fill = 1;
- }
- Result = SetPlay(p,0);
- }
- break;
- case PLAYER_FFWD:
- if (Size == sizeof(bool_t))
- {
- if (*(bool_t*)Data)
- {
- if (!p->Format && p->Wnd && p->PlayListCount)
- Load(p,0,0,0);
- if (!p->Format)
- {
- Result = ERR_INVALID_DATA;
- break;
- }
- p->FFwd = 1;
- }
- else
- {
- p->FFwd = 0;
- if (!p->Play && !p->Bench)
- p->Fill = 1;
- }
- Result = SetPlay(p,0);
- }
- break;
- case PLAYER_LIST_COUNT:
- assert(Size == sizeof(int));
- Result = SetPlayListCount(p,*(int*)Data);
- break;
- case PLAYER_LIST_CURRIDX:
- assert(Size == sizeof(int));
- if (*(int*)Data<p->PlayListCount && *(int*)Data>=0)
- Result = SetCurrent(p,p->PlayIndex[*(int*)Data],0);
- break;
- case PLAYER_LIST_CURRENT:
- assert(Size == sizeof(int));
- Result = SetCurrent(p,*(int*)Data,1);
- break;
- case PLAYER_NEXT:
- t = FindChapter(p,1);
- if (t >= 0)
- Result = Seek2(p,t,NULL,1);
- else
- Result = NextPrev(p,1,0,0);
- break;
- case PLAYER_PREV:
- t = FindChapter(p,-1);
- if (t >= 0)
- Result = Seek2(p,t,NULL,1);
- else
- if (Size>0 && p->Timing && p->Position > TICKSPERSEC*2 && Seek2(p,0,NULL,0)==ERR_NONE)
- {
- ClearStats(p);
- Result = ERR_NONE;
- }
- else
- Result = NextPrev(p,-1,0,0);
- break;
- case PLAYER_STOP:
- SetPlay(p,1);
- if (!Size)
- {
- if (p->Streaming)
- Unload(p,0,0,1);
- else
- NextPrev(p,0,0,0);
- }
- break;
- case PLAYER_RESYNC:
- ReSync(p,1);
- Result = ERR_NONE;
- break;
- case PLAYER_MICRODRIVE: SETVALUE(p->MicroDrive,bool_t,UpdateFormat(p,1)); break;
- case PLAYER_UNDERRUN: SETVALUE(p->UnderRun,int,ERR_NONE); break;
- case PLAYER_AUDIO_UNDERRUN: SETVALUE(p->AudioUnderRun,int,ERR_NONE); break;
- }
- Unlock(p,1);
- break;
- }
- Unlock(p,0);
- return Result;
- }
- static bool_t CmdComment(player_base* p,int Stream,const tchar_t* Name,tchar_t* Value,int ValueLen)
- {
- bool_t Result = 0;
- const uint8_t *i;
- LockEnter(p->LockComment);
- for (i=ARRAYBEGIN(p->Comment,uint8_t);i!=ARRAYEND(p->Comment,uint8_t);i=CommentNext(i))
- if (CommentStream(i)!=INVALID_COMMENT &&
- (Stream<0 || CommentStream(i) == Stream) && tcsicmp(CommentName(i),Name)==0)
- {
- if (Result)
- {
- Result = 0;
- break;
- }
- if (Value)
- tcscpy_s(Value,ValueLen,CommentValue(i));
- Result = 1;
- if (Stream>=0)
- break;
- }
- LockLeave(p->LockComment);
- return Result;
- }
- static void Paint(player_base* p,void* DC,int x0,int y0)
- {
- rect re;
- rect r;
- rect* Exclude = NULL;
- rgbval_t ColorKey;
- #ifdef MULTITHREAD
- assert(p->ProcessLocked != ThreadId() && p->InputLocked != ThreadId());
- #endif
- LockEnter(p->Lock);
- if (p->Primary)
- {
- if (p->VOutput && p->VOutput->Get(p->VOutput,VOUT_OUTPUTRECT,&re,sizeof(re))==ERR_NONE &&
- re.Width>0 && re.Height>0 && !p->TemporaryHidden)
- {
- re.x -= x0;
- re.y -= y0;
- if (p->VOutput->Get(p->VOutput,VOUT_COLORKEY,&ColorKey,sizeof(ColorKey)) == ERR_NONE)
- {
- if (ColorKey != p->ColorKey)
- {
- BrushDelete(p->ColorKeyBrush);
- p->ColorKey = ColorKey;
- p->ColorKeyBrush = BrushCreate(ColorKey);
- }
- WinFill(DC,&re,NULL,p->ColorKeyBrush);
- Exclude = &re;
- }
- else
- if (!p->Clipping && !p->Overlay)
- Exclude = &re;
- }
- r = p->Viewport;
- }
- else
- r = p->SkinViewport;
- LockLeave(p->Lock);
- r.x -= x0;
- r.y -= y0;
- WinFill(DC,&r,Exclude,p->BlackBrush);
- }
- static int Create(player_base* p)
- {
- int i;
- int Model;
- video Desktop;
- QueryDesktop(&Desktop);
- Model = QueryPlatform(PLATFORM_MODEL);
- // default values
- if (QueryPlatform(PLATFORM_TYPENO) == TYPE_SMARTPHONE)
- {
- p->UsedEnough2 = (256*1024)/BLOCKSIZE;
- p->BufferSize2 = (600*1024)/BLOCKSIZE;
- }
- else
- {
- p->UsedEnough2 = (512*1024)/BLOCKSIZE;
- p->BufferSize2 = (2400*1024)/BLOCKSIZE;
- #ifdef TARGET_WINCE
- if (QueryPlatform(PLATFORM_VER) < 421)
- p->BufferSize2 = (1200*1024)/BLOCKSIZE;;
- #endif
- #ifdef TARGET_PALMOS
- p->UsedEnough2 = (256*1024)/BLOCKSIZE;
- p->BufferSize2 = (2000*1024)/BLOCKSIZE;
- #endif
- }
- p->RndSeed = (uint32_t)(GetTimeTick()+GetTimeDate());
- p->Color = NodeEnumObject(NULL,COLOR_ID);
- p->Equalizer = NodeEnumObject(NULL,EQUALIZER_ID);
- p->Player.Enum = (nodeenum)Enum;
- p->Player.Get = (nodeget)Get;
- p->Player.Set = (nodeset)Set;
- p->Player.Paint = (playerpaint)Paint;
- p->Player.CommentByName = (playercomment)CmdComment;
- p->Player.ListSwap = (playerswap)ListSwap;
- p->Player.Process = (playerprocess)ProcessThread;
- p->MDBufferSize2 = (16000*1024)/BLOCKSIZE;
- p->BurstStart = (1500*1024)/BLOCKSIZE;
- p->UnderRun = PERCENT_ONE*7/10;
- p->AudioUnderRun = 64;
- p->MoveFFwd = p->MoveBack = TICKSPERSEC*10;
- p->PlayAtOpen = 0;
- p->PlayAtOpenFull = 0;
- p->KeepList = 1;
- p->KeepPlayAudio = 1;
- #if defined(TARGET_WIN32)
- p->SingleClickFullScreen = 0;
- p->KeepPlayVideo = 1;
- p->ShowInBackground = 1;
- p->AutoPreRotate = 0;
- #else
- p->SingleClickFullScreen = 1;
- p->KeepPlayVideo = 0;
- p->ShowInBackground = 0;
- p->AutoPreRotate = Desktop.Width != Desktop.Height;
- #endif
- p->FullZoom.Den = 1;
- p->FullZoom.Num = 0;
- p->SkinZoom.Den = 1;
- p->SkinZoom.Num = 0;
- p->VideoQuality = 2;
- #if !defined(SH3) && !defined(MIPS)
- p->AudioQuality = 2;
- p->SmoothZoom50 = 1;
- p->SmoothZoomAlways = 0;
- #else
- p->AudioQuality = 1;
- p->SmoothZoom50 = 0;
- p->SmoothZoomAlways = 0;
- #if defined(SH3)
- p->FullZoom.Num = p->SkinZoom.Num = 1;
- #endif
- #endif
- #ifdef TARGET_SYMBIAN
- p->Volume = 50;
- #else
- p->Volume = 90;
- #endif
- p->Repeat = 1;
- #ifdef TARGET_WIN32
- p->Aspect.Num = 0;
- #else
- p->Aspect.Num = 1;
- #ifdef ARM
- // use aspect with hardware scaler overlay drivers...
- if (NodeEnumClass(NULL,FOURCC('A','T','I','4')) ||
- NodeEnumClass(NULL,FOURCC('G','E','2','D')) ||
- NodeEnumClass(NULL,FOURCC('A','H','I','_')) ||
- NodeEnumClass(NULL,FOURCC('2','7','0','G')))
- p->Aspect.Num = 0;
- #endif
- #endif
- p->Aspect.Den = 1;
- p->PlaySpeed.Num = 1;
- p->PlaySpeed.Den = 1;
- p->FFwdSpeed.Num = 2;
- p->FFwdSpeed.Den = 1;
- p->SeekAfterSync.Num = -1;
- p->Primary = 1;
- p->SkinDir = -1;
- p->Foreground = 1;
- if (Model==MODEL_TUNGSTEN_T3)
- p->FullDir = -1; // special case because it can be slided into square format
- else
- {
- p->FullDir = GetOrientation();
- if (Desktop.Width < Desktop.Height)
- {
- if (GetHandedness())
- p->FullDir = CombineDir(p->FullDir,DIR_SWAPXY | DIR_MIRRORLEFTRIGHT,0);
- else
- p->FullDir = CombineDir(p->FullDir,DIR_SWAPXY | DIR_MIRRORUPDOWN,0);
- }
- }
- for (i=0;i<PACKET_MAX;++i)
- p->Selected[i] = -1;
- p->CurrentChanged = 1;
- p->Position = -1;
- p->AOutputIdMax = p->AOutputId = NodeEnumClass(NULL,AOUT_CLASS);
- p->VOutputIdMax = p->VOutputId = NodeEnumClass(NULL,VOUT_CLASS);
- p->VideoAccel = VOutIDCT(p->VOutputId);
- #ifdef MULTITHREAD
- p->LockProcessCount = 0;
- p->LockInputCount = 0;
- p->LockComment = LockCreate();
- p->Lock = LockCreate();
- p->LockInput = LockCreate();
- p->LockProcess = LockCreate();
- p->EventProcess = EventCreate(0,0);
- p->EventRunProcess = EventCreate(1,0);
- p->EventRunInput = EventCreate(1,0);
- #endif
- p->ColorKey = RGB_NULL;
- p->ColorKeyBrush = NULL;
- p->BlackBrush = BrushCreate(0);
- UpdateFormat(p,0);
- UpdateRunProcess(p);
- UpdateRunInput(p);
- return ERR_NONE;
- }
- static void Delete(player_base* p)
- {
- UpdateWnd(p,NULL);
- #ifdef MULTITHREAD
- EventClose(p->EventRunInput);
- EventClose(p->EventRunProcess);
- EventClose(p->EventProcess);
- LockDelete(p->Lock);
- LockDelete(p->LockInput);
- LockDelete(p->LockProcess);
- LockDelete(p->LockComment);
- #endif
- BrushDelete(p->ColorKeyBrush);
- BrushDelete(p->BlackBrush);
- free(p->PlayList);
- free(p->PlayIndex);
- }
- void PlayerSaveList(player* Player,const tchar_t* Path,int Class)
- {
- playlist* Playlist = (playlist*)NodeCreate(Class);
- if (Playlist)
- {
- if (Playlist->WriteList)
- {
- stream* Input = GetStream(Path,0);
- if (Input)
- {
- bool_t b = 1;
- if (Input->Set(Input,STREAM_CREATE,&b,sizeof(b)) == ERR_NONE &&
- Input->Set(Input,STREAM_URL,Path,sizeof(tchar_t)*(tcslen(Path)+1)) == ERR_NONE &&
- Playlist->Set(Playlist,PLAYLIST_STREAM,&Input,sizeof(Input)) == ERR_NONE)
- {
- tchar_t Base[MAXPATH];
- tchar_t Rel[MAXPATH];
- tchar_t URL[MAXPATH];
- tchar_t Title[256];
- tick_t Length;
- int No,Count;
- SplitURL(Path,Base,TSIZEOF(Base),Base,TSIZEOF(Base),NULL,0,NULL,0);
- Player->Get(Player,PLAYER_LIST_COUNT,&Count,sizeof(Count));
- for (No=0;No<Count;++No)
- {
- if (Player->Get(Player,PLAYER_LIST_URL+No,URL,sizeof(URL))==ERR_NONE)
- {
- RelPath(Rel,TSIZEOF(Rel),URL,Base);
- Title[0] = 0;
- Length = -1;
- Player->Get(Player,PLAYER_LIST_TITLE+No,Title,sizeof(Title));
- Player->Get(Player,PLAYER_LIST_LENGTH+No,&Length,sizeof(Length));
- Playlist->WriteList(Playlist,Rel,Title,Length);
- }
- }
- Playlist->WriteList(Playlist,NULL,NULL,0);
- Playlist->Set(Playlist,PLAYLIST_STREAM,NULL,0);
- }
- Input->Set(Input,STREAM_URL,NULL,0);
- NodeDelete((node*)Input);
- }
- }
- Playlist->Set(Playlist,PLAYLIST_STREAM,NULL,0);
- NodeDelete((node*)Playlist);
- }
- }
- int PlayerAddDir(player* Player,int Index, const tchar_t* Path, const tchar_t* Exts, bool_t ExtFilter, int Deep)
- {
- streamdir DirItem;
- stream* Stream;
- if (Deep < MAXDIRDEEP) // fail safe
- {
- Stream = GetStream(Path,1);
- if (Stream)
- {
- tchar_t Base[MAXPATH];
- int Result = Stream->EnumDir(Stream,Path,Exts,ExtFilter,&DirItem);
- if (Result == ERR_NONE)
- {
- tcscpy_s(Base,TSIZEOF(Base),Path);
- Stream->Get(Stream,STREAM_BASE,Base,sizeof(Base));
- }
- while (Result == ERR_NONE)
- {
- tchar_t PathItem[MAXPATH];
- AbsPath(PathItem,TSIZEOF(PathItem),DirItem.FileName,Base);
- if (tcsncmp(PathItem,Base,tcslen(Base))==0) // don't go to other sites with http
- {
- if (DirItem.Size < 0)
- Index = PlayerAddDir(Player,Index,PathItem,Exts,ExtFilter,Deep+1);
- else
- Index = PlayerAdd(Player,Index,PathItem,DirItem.DisplayName);
- }
- Result = Stream->EnumDir(Stream,NULL,NULL,0,&DirItem);
- }
- NodeDelete((node*)Stream);
- }
- }
- return Index;
- }
- int PlayerAdd(player* Player,int Index, const tchar_t* Path, const tchar_t* Title)
- {
- stream* Input;
- array List;
- // process playlist files
- NodeEnumClassEx(&List,PLAYLIST_CLASS,NULL,Path,NULL,0);
- if (!ARRAYEMPTY(List) && (Input = GetStream(Path,0))!=NULL)
- {
- if (Input->Set(Input,STREAM_URL,Path,sizeof(tchar_t)*(tcslen(Path)+1)) == ERR_NONE &&
- LoadPlaylist(Player,&List,Input,&Index,Path,NULL,0))
- Path = NULL;
- Input->Set(Input,STREAM_URL,NULL,0);
- NodeDelete((node*)Input);
- }
- ArrayClear(&List);
- if (Path)
- {
- Player->Set(Player,PLAYER_LIST_URL+Index,Path,sizeof(tchar_t)*(tcslen(Path)+1));
- Player->Set(Player,PLAYER_LIST_TITLE+Index,Title,Title?sizeof(tchar_t)*(tcslen(Title)+1):0);
- ++Index;
- }
- return Index;
- }
- const tchar_t* PlayerComment(int Code)
- {
- switch (Code)
- {
- case COMMENT_TITLE: return T("TITLE");
- case COMMENT_ARTIST: return T("ARTIST");
- case COMMENT_ALBUM: return T("ALBUM");
- case COMMENT_GENRE: return T("GENRE");
- case COMMENT_LANGUAGE: return T("LANGUAGE");
- case COMMENT_AUTHOR: return T("AUTHOR");
- case COMMENT_COPYRIGHT: return T("COPYRIGHT");
- case COMMENT_PRIORITY: return T("PRIORITY");
- case COMMENT_YEAR: return T("YEAR");
- case COMMENT_TRACK: return T("TRACK");
- case COMMENT_COMMENT: return T("COMMENT");
- case COMMENT_COVER: return T("COVER");
- case COMMENT_REDIRECT: return T("REDIRECT");
- }
- return NULL;
- }
- tick_t PlayerGetChapter(player* p,int No,tchar_t* OutName,int OutNameLen)
- {
- tchar_t Chapter[64];
- tchar_t s[64];
- int Hour,Min,Sec,MSec=0;
- if (No>0)
- {
- stprintf_s(Chapter,TSIZEOF(Chapter),T("CHAPTER%02d"),No);
- if (p->CommentByName(p,-1,Chapter,s,TSIZEOF(s)) &&
- stscanf(s,T("%d:%d:%d.%d"),&Hour,&Min,&Sec,&MSec)>=3)
- {
- if (OutName)
- {
- stprintf_s(Chapter,TSIZEOF(Chapter),T("CHAPTER%02dNAME"),No);
- if (!p->CommentByName(p,-1,Chapter,OutName,OutNameLen))
- stprintf_s(OutName,OutNameLen,T("%d"),No);
- }
- return Scale(((Hour*60+Min)*60+Sec)*1000+MSec,TICKSPERSEC,1000);
- }
- }
- return -1;
- }
- bool_t PlayerGetStream(player* Player,int No,packetformat* OutFormat,tchar_t* OutName,int OutNameLen,int* OutPri)
- {
- tchar_t s[256];
- node* Format;
- packetformat PacketFormat;
- int i;
- if (Player->Get(Player,PLAYER_FORMAT,&Format,sizeof(Format))==ERR_NONE && Format)
- {
- int Count[PACKET_MAX];
- memset(Count,0,sizeof(Count));
- for (i=0;Format->Get(Format,(FORMAT_STREAM+i)|PIN_FORMAT,&PacketFormat,sizeof(PacketFormat))==ERR_NONE;++i)
- {
- ++Count[PacketFormat.Type];
- if (i==No)
- {
- if (OutPri)
- {
- if (Player->CommentByName(Player,i,PlayerComment(COMMENT_PRIORITY),s,TSIZEOF(s)))
- *OutPri = StringToInt(s,0);
- else
- *OutPri = 0;
- }
- if (OutName)
- {
- if (Player->CommentByName(Player,i,PlayerComment(COMMENT_LANGUAGE),s,TSIZEOF(s)) ||
- Player->CommentByName(Player,i,PlayerComment(COMMENT_TITLE),s,TSIZEOF(s)))
- tcscpy_s(OutName,OutNameLen,s);
- else
- stprintf_s(OutName,OutNameLen,T("%s %d"),LangStr(PLAYER_ID,STREAM_NAME+PacketFormat.Type),Count[PacketFormat.Type]);
- }
- if (OutFormat)
- *OutFormat = PacketFormat;
- return 1;
- }
- }
- }
- return 0;
- }
- static const nodedef Player =
- {
- sizeof(player_base)|CF_GLOBAL|CF_SETTINGS,
- PLAYER_ID,
- NODE_CLASS,
- PRI_MAXIMUM+600,
- (nodecreate)Create,
- (nodedelete)Delete,
- };
- typedef struct playerbuffer
- {
- node Node;
- node* Player;
- } playerbuffer;
- static int BufferEnum(playerbuffer* p, int* No, datadef* Param)
- {
- int Result = NodeEnumTable(No,Param,BufferParams);
- if (Result == ERR_NONE)
- Param->Name = LangStr(PLAYER_ID,Param->No);
- return Result;
- }
- static int BufferGet(playerbuffer* p, int No, void* Data, int Size)
- {
- return p->Player->Get(p->Player,No,Data,Size);
- }
- static int BufferSet(playerbuffer* p, int No, const void* Data, int Size)
- {
- return p->Player->Set(p->Player,No,Data,Size);
- }
- static int BufferCreate(playerbuffer* p)
- {
- p->Player = Context()->Player;
- p->Node.Enum = (nodeenum)BufferEnum;
- p->Node.Get = (nodeget)BufferGet;
- p->Node.Set = (nodeset)BufferSet;
- return ERR_NONE;
- }
- static const nodedef Buffer =
- {
- sizeof(playerbuffer)|CF_GLOBAL|CF_SETTINGS,
- PLAYER_BUFFER_ID,
- NODE_CLASS,
- PRI_MAXIMUM+60,
- (nodecreate)BufferCreate
- };
- void Player_Init()
- {
- NodeRegisterClass(&Player);
- Context()->Player = NodeEnumObject(NULL,PLAYER_ID);
- NodeRegisterClass(&Buffer);
- }
- void Player_Done()
- {
- Context()->Player = NULL;
- NodeUnRegisterClass(PLAYER_ID);
- NodeUnRegisterClass(PLAYER_BUFFER_ID);
- }