Video player task retired; minor legacy video semantical changes; improved video playback jitter compensation

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3297 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
avolkov
2009-11-11 08:03:22 +00:00
parent cf28c52a8f
commit 393030ff6c
9 changed files with 134 additions and 156 deletions
+1 -2
View File
@@ -615,8 +615,7 @@ dukv_Open (THIS_PTR, uio_DirHandle *dir, const char *filename)
This->length = (float) dukv->cframes / DUCK_GENERAL_FPS;
This->frame_count = dukv->cframes;
This->max_frame_wait =
(uint32) (1000.0f / DUCK_GENERAL_FPS * 1.1);
This->interframe_wait = (uint32) (1000.0 / DUCK_GENERAL_FPS);
dukv->inbuf = HMalloc (DUCK_MAX_FRAME_SIZE);
dukv->decbuf = HMalloc (
+1 -2
View File
@@ -61,8 +61,7 @@ StopLegacyVideo (LEGACY_VIDEO_REF ref)
{
if (!ref)
return;
if (TFB_VideoPlaying (ref->vidref))
VidStop ();
VidStop ();
DestroyVideo (ref->vidref);
if (ref->speechref)
+11
View File
@@ -74,6 +74,14 @@ VidPlaying (void)
return NULL_VIDEO_REF;
}
BOOLEAN
VidProcessFrame (void)
{
if (!_cur_video)
return FALSE;
return TFB_ProcessVideoFrame (_cur_video);
}
// return current video position in milliseconds
DWORD
VidGetPosition (void)
@@ -173,6 +181,9 @@ DestroyVideo (VIDEO_REF vid)
if (!vid)
return FALSE;
// just some armouring; should already be stopped
TFB_StopVideo (vid);
VideoDecoder_Free (vid->decoder);
DestroyMutex (vid->guard);
HFree (vid);
+1 -3
View File
@@ -23,7 +23,6 @@
#include "types.h"
#include "videodec.h"
#include "libs/sound/sound.h"
#include "libs/tasklib.h"
typedef struct tfb_videoclip
@@ -36,11 +35,9 @@ typedef struct tfb_videoclip
RECT dst_rect; // destination screen rect
RECT src_rect; // source rect
MUSIC_REF hAudio;
Task play_task;
uint32 frame_time; // time when next frame should be rendered
TFB_Image* frame; // frame preped and optimized for rendering
uint32 cur_frame; // index of frame currently displayed
uint32 max_frame_wait;
bool playing;
bool own_audio;
uint32 loop_frame; // frame index to loop from
@@ -48,6 +45,7 @@ typedef struct tfb_videoclip
Mutex guard;
uint32 want_frame; // audio-signaled desired frame index
int lag_cnt; // N of frames video is behind or ahead of audio
void* data; // user-defined data
+1 -1
View File
@@ -74,7 +74,7 @@ struct tfb_videodecoder
uint32 w, h;
float length; // total length in seconds
uint32 frame_count;
uint32 max_frame_wait; // maximum interframe delay in msecs
uint32 interframe_wait; // nominal interframe delay in msecs
bool audio_synced;
// decoder callbacks
TFB_VideoCallbacks callbacks;
+113 -144
View File
@@ -55,163 +55,130 @@ static const TFB_SoundCallbacks vp_AudioCBs =
vp_QueueBuffer
};
// inter-thread param guarded by mutex
static Semaphore vp_interthread_lock = 0;
static void* vp_interthread_clip = NULL;
bool
TFB_InitVideoPlayer (void)
{
// creation probably needs better handling
vp_interthread_lock = CreateSemaphore (1, "inter-thread param lock",
SYNC_CLASS_VIDEO);
return vp_interthread_lock != 0;
// now just a stub
return true;
}
void
TFB_UninitVideoPlayer (void)
{
DestroySemaphore (vp_interthread_lock);
// now just a stub
}
// audio-synced video playback task
// the frame rate and timing is dictated by the audio
static int
as_video_play_task (void *data)
static inline sint32
msecToTimeCount (sint32 msec)
{
Task task = (Task) data;
volatile TFB_VideoClip* vid;
return msec * ONE_SECOND / 1000;
}
// audio-synced video playback frame function
// the frame rate and timing is dictated by the audio
static bool
processAudioSyncedFrame (VIDEO_REF vid)
{
#define MAX_FRAME_LAG 8
#define LAG_FRACTION 6
#define SYNC_BIAS 1 / 3
int ret;
uint32 want_frame;
TimeCount TimeOut;
int bTerm;
uint32 clagged;
uint32 prev_want_frame;
sint32 wait_msec;
CONTEXT oldContext;
TimeCount Now = GetTimeCounter ();
// snatch the clip pointer and release
vid = vp_interthread_clip;
vp_interthread_clip = NULL;
ClearSemaphore (vp_interthread_lock);
if (!vid->playing)
return false;
if (Now < vid->frame_time)
return true; // not time yet
LockMutex (vid->guard);
want_frame = vid->want_frame;
if (vid->hAudio)
PLRPlaySong (vid->hAudio, FALSE, 1);
UnlockMutex (vid->guard);
clagged = 0;
if (want_frame >= vid->decoder->frame_count)
{
vid->playing = false;
return false;
}
// this works like so (audio-synced):
// 1. you call VideoDecoder_Seek() [when necessary] and
// VideoDecoder_Decode()
// 2. wait till the audio signals it's time for this frame
// or the maximum inter-frame timeout elapses; the timeout
// is necessary because the audio signaling is not precise
// (see vp_AudioStart, vp_AudioEnd, vp_BufferTag)
// 3. output the frame; if the timeout elapsed, increment the
// the lag counter
// 4. set the next frame timeout; lag counter increases the
// timeout to allow audio to catch up
// 2. wait till it's time for this frame to be drawn
// the timeout is necessary because the audio signaling is not
// precise (see vp_AudioStart, vp_AudioEnd, vp_BufferTag)
// 3. output the frame; if the audio is behind, the lag counter
// goes up; if the video is behind, the lag counter goes down
// 4. set the next frame timeout; lag counter increases or
// decreases the timeout to allow audio or video to catch up
// 5. on a seek operation, the audio stream is moved to the
// correct position and then the audio signals the frame
// that should be rendered
// The system of timeouts and lag counts should make the video
// *relatively* smooth
//
ret = VideoDecoder_Decode (vid->decoder);
TimeOut = GetTimeCounter () + vid->decoder->max_frame_wait *
ONE_SECOND / 1000;
while (!(bTerm = Task_ReadState (task, TASK_EXIT)) && ret > 0)
prev_want_frame = vid->cur_frame - vid->lag_cnt;
if (want_frame > prev_want_frame - MAX_FRAME_LAG
&& want_frame <= prev_want_frame + MAX_FRAME_LAG)
{
// wait till its time to render next frame
while (!(bTerm = Task_ReadState (task, TASK_EXIT))
&& want_frame == vid->cur_frame - clagged
&& TimeOut > GetTimeCounter ())
{
TaskSwitch ();
LockMutex (vid->guard);
want_frame = vid->want_frame;
UnlockMutex (vid->guard);
}
if (bTerm)
break;
if (want_frame == vid->cur_frame - clagged)
{ // timed out - draw the next frame
++clagged;
}
else if (want_frame > vid->cur_frame - clagged
&& want_frame <= vid->cur_frame)
{
// catching up
clagged = vid->cur_frame - want_frame;
// try again
continue;
}
else if (want_frame != vid->cur_frame + 1)
{ // out of sequence frame, let's get it
vid->cur_frame = VideoDecoder_SeekFrame (
vid->decoder, want_frame);
ret = VideoDecoder_Decode (vid->decoder);
clagged = 0;
}
else
{
clagged = 0;
}
vid->cur_frame = vid->decoder->cur_frame;
// draw the frame
if (ret > 0)
{
CONTEXT oldContext;
LockMutex (GraphicsLock);
// We have the cliprect precalculated and don't need the rest
oldContext = SetContext (NULL);
TFB_DrawScreen_Image (vid->frame,
vid->dst_rect.corner.x, vid->dst_rect.corner.y,
GSCALE_IDENTITY, NULL, TFB_SCREEN_MAIN);
SetContext (oldContext);
UnlockMutex (GraphicsLock);
FlushGraphics (); // needed to prevent half-frame updates
}
// increase timeout with lag-count to allow audio to catch up
TimeOut = GetTimeCounter () + vid->decoder->max_frame_wait *
ONE_SECOND / 1000 + clagged * ONE_SECOND / 100;
ret = VideoDecoder_Decode (vid->decoder);
// we will draw the next frame right now, thus +1
vid->lag_cnt = vid->cur_frame + 1 - want_frame;
}
if (vid->hAudio)
PLRStop (vid->hAudio);
vid->playing = false;
else
{ // out of sequence frame, let's get it
vid->lag_cnt = 0;
vid->cur_frame = VideoDecoder_SeekFrame (vid->decoder, want_frame);
ret = VideoDecoder_Decode (vid->decoder);
if (ret < 0)
{ // decoder returned a failure
vid->playing = false;
return false;
}
}
vid->cur_frame = vid->decoder->cur_frame;
FinishTask (task);
// draw the frame
LockMutex (GraphicsLock);
// We have the cliprect precalculated and don't need the rest
oldContext = SetContext (NULL);
TFB_DrawScreen_Image (vid->frame,
vid->dst_rect.corner.x, vid->dst_rect.corner.y,
GSCALE_IDENTITY, NULL, TFB_SCREEN_MAIN);
SetContext (oldContext);
UnlockMutex (GraphicsLock);
FlushGraphics (); // needed to prevent half-frame updates
return 0;
// increase interframe with positive lag-count to allow audio to catch up
// decrease interframe with negative lag-count to allow video to catch up
wait_msec = vid->decoder->interframe_wait
- (int)vid->decoder->interframe_wait * SYNC_BIAS
+ (int)vid->decoder->interframe_wait * vid->lag_cnt / LAG_FRACTION;
vid->frame_time = Now + msecToTimeCount (wait_msec);
ret = VideoDecoder_Decode (vid->decoder);
if (ret < 0)
{
// TODO: decide what to do on error
}
return vid->playing;
}
// audio-independent video playback task
// audio-independent video playback frame function
// the frame rate and timing is dictated by the video decoder
static int
video_play_task (void *data)
static bool
processMuteFrame (VIDEO_REF vid)
{
Task task = (Task) data;
TFB_VideoClip* vid;
int ret;
TimeCount Now = GetTimeCounter ();
// snatch the clip pointer and release
vid = vp_interthread_clip;
vp_interthread_clip = NULL;
ClearSemaphore (vp_interthread_lock);
LockMutex (vid->guard);
if (vid->hAudio)
PLRPlaySong (vid->hAudio, (vid->loop_frame != VID_NO_LOOP), 1);
UnlockMutex (vid->guard);
if (!vid->playing)
return false;
// this works like so:
// 1. you call VideoDecoder_Seek() [when necessary] and
@@ -222,14 +189,10 @@ video_play_task (void *data)
// On a seek operation, the decoder should reset its internal
// clock and call vp_GetTicks() again
//
ret = VideoDecoder_Decode (vid->decoder);
while (!Task_ReadState (task, TASK_EXIT) && ret > 0)
if (Now >= vid->frame_time)
{
CONTEXT oldContext;
// wait till its time to render next frame
SleepThreadUntil (vid->frame_time);
vid->cur_frame = vid->decoder->cur_frame;
LockMutex (GraphicsLock);
@@ -246,14 +209,11 @@ video_play_task (void *data)
VideoDecoder_SeekFrame (vid->decoder, vid->loop_to);
ret = VideoDecoder_Decode (vid->decoder);
if (ret <= 0)
vid->playing = false;
}
vid->playing = false;
if (vid->hAudio)
PLRStop (vid->hAudio);
FinishTask (task);
return 0;
return vid->playing;
}
bool
@@ -264,6 +224,8 @@ TFB_PlayVideo (VIDEO_REF vid, uint32 x, uint32 y)
RECT vid_r = {{0, 0}, {ScreenWidth, ScreenHeight}};
RECT dr = {{x, y}, {vid->w, vid->h}};
RECT sr;
bool loop_music = false;
int ret;
if (!vid)
return false;
@@ -318,24 +280,21 @@ TFB_PlayVideo (VIDEO_REF vid, uint32 x, uint32 y)
TFB_SetSoundSampleData (*vid->hAudio, (intptr_t)vid);
}
SetSemaphore (vp_interthread_lock);
vp_interthread_clip = vid;
// get the first frame
ret = VideoDecoder_Decode (vid->decoder);
if (ret < 0)
return false;
vid->playing = true;
loop_music = !vid->decoder->audio_synced && vid->loop_frame != VID_NO_LOOP;
if (vid->hAudio)
PLRPlaySong (vid->hAudio, loop_music, 1);
if (vid->decoder->audio_synced)
vid->play_task = AssignTask (
as_video_play_task, 4096, "a/s video player");
else
vid->play_task = AssignTask (
video_play_task, 4096, "video player");
if (!vid->play_task)
{
vid->playing = false;
ClearSemaphore (vp_interthread_lock);
TFB_StopVideo (vid);
return false;
// draw the first frame now
vid->frame_time = GetTimeCounter ();
}
return true;
@@ -348,8 +307,6 @@ TFB_StopVideo (VIDEO_REF vid)
return;
vid->playing = false;
if (vid->play_task)
ConcludeTask (vid->play_task);
if (vid->hAudio)
{
@@ -377,6 +334,18 @@ TFB_VideoPlaying (VIDEO_REF vid)
return vid->playing;
}
bool
TFB_ProcessVideoFrame (VIDEO_REF vid)
{
if (!vid)
return false;
if (vid->decoder->audio_synced)
return processAudioSyncedFrame (vid);
else
return processMuteFrame (vid);
}
uint32
TFB_GetVideoPosition (VIDEO_REF vid)
{
+1
View File
@@ -24,6 +24,7 @@ extern void TFB_UninitVideoPlayer (void);
extern bool TFB_PlayVideo (VIDEO_REF VidRef, uint32 x, uint32 y);
extern void TFB_StopVideo (VIDEO_REF VidRef);
extern bool TFB_VideoPlaying (VIDEO_REF VidRef);
extern bool TFB_ProcessVideoFrame (VIDEO_REF vid);
extern uint32 TFB_GetVideoPosition (VIDEO_REF VidRef);
extern bool TFB_SeekVideo (VIDEO_REF VidRef, uint32 pos);
+1
View File
@@ -47,6 +47,7 @@ extern VIDEO_TYPE VidPlayEx (VIDEO_REF VidRef, MUSIC_REF AudRef,
#define VID_NO_LOOP (0U-1)
extern void VidStop (void);
extern VIDEO_REF VidPlaying (void);
extern BOOLEAN VidProcessFrame (void);
extern DWORD VidGetPosition (void); // position in milliseconds
extern BOOLEAN VidSeek (DWORD pos); // position in milliseconds
+4 -4
View File
@@ -822,8 +822,6 @@ DoVideoInput (void *pIS)
if (!PlayingLegacyVideo (pVIS->CurVideo))
{ // Video probably finished
// Have to call VidStop anyway to cleanup
VidStop ();
return FALSE;
}
@@ -832,7 +830,6 @@ DoVideoInput (void *pIS)
|| PulsedInputState.menu[KEY_MENU_SPECIAL]
|| (GLOBAL (CurrentActivity) & CHECK_ABORT))
{ // abort movie
VidStop ();
return FALSE;
}
else if (PulsedInputState.menu[KEY_MENU_LEFT]
@@ -850,7 +847,10 @@ DoVideoInput (void *pIS)
}
else
{
SleepThread (ONE_SECOND / 30);
if (!VidProcessFrame ())
return FALSE;
SleepThread (ONE_SECOND / 40);
}
return TRUE;