From b0d9f4102074e85cf4594ad712f0239fa93082c9 Mon Sep 17 00:00:00 2001 From: Johns Date: Fri, 2 Mar 2012 18:16:20 +0100 Subject: [PATCH] Rewrote video/audio start code. --- ChangeLog | 1 + audio.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++------ audio.h | 3 +- video.c | 63 +++++++++++++++++----------- 4 files changed, 148 insertions(+), 39 deletions(-) diff --git a/ChangeLog b/ChangeLog index f0bcfb7..81c1334 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ User johns Date: + Rewrote video/audio start code. Add support for attach/detach plugin. OSS needs bigger audio buffers. Improved audio drift correction support. diff --git a/audio.c b/audio.c index 8767f4e..d2e8323 100644 --- a/audio.c +++ b/audio.c @@ -107,9 +107,11 @@ typedef struct _audio_module_ void (*Thread) (void); ///< module thread handler void (*Enqueue) (const void *, int); ///< enqueue samples for output + void (*VideoReady) (void); ///< video ready, start audio void (*FlushBuffers) (void); ///< flush sample buffers void (*Poller) (void); ///< output poller int (*FreeBytes) (void); ///< number of bytes free in buffer + int (*UsedBytes) (void); ///< number of bytes used in buffer uint64_t(*GetDelay) (void); ///< get current audio delay void (*SetVolume) (int); ///< set output volume int (*Setup) (int *, int *, int); ///< setup channels, samplerate @@ -137,6 +139,7 @@ static const char *AudioMixerDevice; ///< alsa/OSS mixer device name static const char *AudioMixerChannel; ///< alsa/OSS mixer channel name static volatile char AudioRunning; ///< thread running / stopped static volatile char AudioPaused; ///< audio paused +static volatile char AudioVideoIsReady; ///< video ready start early static unsigned AudioSampleRate; ///< audio sample rate in hz static unsigned AudioChannels; ///< number of audio channels static const int AudioBytesProSample = 2; ///< number of bytes per sample @@ -152,7 +155,6 @@ static const int AudioThread; ///< dummy audio thread #endif extern int VideoAudioDelay; ///< import audio/video delay -extern int VideoGetBuffers(void); ///< Get number of input buffers. #ifdef USE_AUDIORING @@ -292,16 +294,16 @@ static int AlsaAddToRingbuffer(const void *samples, int count) } if (!AudioRunning) { - Debug(3, "audio/alsa: start %4zd ms %d v-buf\n", + Debug(4, "audio/alsa: start %4zdms\n", (RingBufferUsedBytes(AlsaRingBuffer) * 1000) - / (AudioSampleRate * AudioChannels * AudioBytesProSample), - VideoGetBuffers()); + / (AudioSampleRate * AudioChannels * AudioBytesProSample)); + // forced start - if (AlsaStartThreshold * 3 < RingBufferUsedBytes(AlsaRingBuffer)) { + if (AlsaStartThreshold * 2 < RingBufferUsedBytes(AlsaRingBuffer)) { return 1; } // enough video + audio buffered - if (VideoGetBuffers() > 1 + if (AudioVideoIsReady && AlsaStartThreshold < RingBufferUsedBytes(AlsaRingBuffer)) { // restart play-back return 1; @@ -446,6 +448,7 @@ static void AlsaFlushBuffers(void) } } AudioRunning = 0; + AudioVideoIsReady = 0; AudioPTS = INT64_C(0x8000000000000000); } @@ -470,6 +473,14 @@ static int AlsaFreeBytes(void) return AlsaRingBuffer ? RingBufferFreeBytes(AlsaRingBuffer) : INT32_MAX; } +/** +** Get used bytes in audio output. +*/ +static int AlsaUsedBytes(void) +{ + return AlsaRingBuffer ? RingBufferUsedBytes(AlsaRingBuffer) : 0; +} + #if 0 //---------------------------------------------------------------------------- @@ -706,6 +717,26 @@ static void AlsaThreadEnqueue(const void *samples, int count) } } +/** +** Video is ready, start audio if possible, +*/ +static void AlsaVideoReady(void) +{ + if (AudioSampleRate && AudioChannels) { + Debug(3, "audio/alsa: start %4zdms video start\n", + (RingBufferUsedBytes(AlsaRingBuffer) * 1000) + / (AudioSampleRate * AudioChannels * AudioBytesProSample)); + } + + if (!AudioRunning) { + // enough video + audio buffered + if (AlsaStartThreshold < RingBufferUsedBytes(AlsaRingBuffer)) { + AudioRunning = 1; + pthread_cond_signal(&AudioStartCond); + } + } +} + /** ** Flush alsa buffers with thread. */ @@ -1245,13 +1276,16 @@ static const AudioModule AlsaModule = { #ifdef USE_AUDIO_THREAD .Thread = AlsaThread, .Enqueue = AlsaThreadEnqueue, + .VideoReady = AlsaVideoReady, .FlushBuffers = AlsaThreadFlushBuffers, #else .Enqueue = AlsaEnqueue, + .VideoReady = AlsaVideoReady, .FlushBuffers = AlsaFlushBuffers, #endif .Poller = AlsaPoller, .FreeBytes = AlsaFreeBytes, + .UsedBytes = AlsaUsedBytes, .GetDelay = AlsaGetDelay, .SetVolume = AlsaSetVolume, .Setup = AlsaSetup, @@ -1308,16 +1342,16 @@ static int OssAddToRingbuffer(const void *samples, int count) } if (!AudioRunning) { - Debug(3, "audio/oss: start %4zd ms %d v-buf\n", + Debug(4, "audio/oss: start %4zdms\n", (RingBufferUsedBytes(OssRingBuffer) * 1000) - / (AudioSampleRate * AudioChannels * AudioBytesProSample), - VideoGetBuffers()); + / (AudioSampleRate * AudioChannels * AudioBytesProSample)); + // forced start - if (OssStartThreshold * 3 < RingBufferUsedBytes(OssRingBuffer)) { + if (OssStartThreshold * 2 < RingBufferUsedBytes(OssRingBuffer)) { return 1; } // enough video + audio buffered - if (VideoGetBuffers() > 1 + if (AudioVideoIsReady && OssStartThreshold < RingBufferUsedBytes(OssRingBuffer)) { // restart play-back return 1; @@ -1392,6 +1426,7 @@ static void OssFlushBuffers(void) } } AudioRunning = 0; + AudioVideoIsReady = 0; AudioPTS = INT64_C(0x8000000000000000); } @@ -1450,6 +1485,14 @@ static int OssFreeBytes(void) return OssRingBuffer ? RingBufferFreeBytes(OssRingBuffer) : INT32_MAX; } +/** +** Get used bytes in audio output. +*/ +static int OssUsedBytes(void) +{ + return OssRingBuffer ? RingBufferUsedBytes(OssRingBuffer) : 0; +} + #ifdef USE_AUDIO_THREAD //---------------------------------------------------------------------------- @@ -1520,6 +1563,26 @@ static void OssThreadEnqueue(const void *samples, int count) } } +/** +** Video is ready, start audio if possible, +*/ +static void OssVideoReady(void) +{ + if (AudioSampleRate && AudioChannels) { + Debug(3, "audio/oss: start %4zdms video start\n", + (RingBufferUsedBytes(OssRingBuffer) * 1000) + / (AudioSampleRate * AudioChannels * AudioBytesProSample)); + } + + if (!AudioRunning) { + // enough video + audio buffered + if (OssStartThreshold < RingBufferUsedBytes(OssRingBuffer)) { + AudioRunning = 1; + pthread_cond_signal(&AudioStartCond); + } + } +} + /** ** Flush OSS buffers with thread. */ @@ -1881,13 +1944,16 @@ static const AudioModule OssModule = { #ifdef USE_AUDIO_THREAD .Thread = OssThread, .Enqueue = OssThreadEnqueue, + .VideoReady = OssVideoReady, .FlushBuffers = OssThreadFlushBuffers, #else .Enqueue = OssEnqueue, + .VideoReady = OssVideoReady, .FlushBuffers = OssFlushBuffers, #endif .Poller = OssPoller, .FreeBytes = OssFreeBytes, + .UsedBytes = OssUsedBytes, .GetDelay = OssGetDelay, .SetVolume = OssSetVolume, .Setup = OssSetup, @@ -1923,6 +1989,14 @@ static int NoopFreeBytes(void) return INT32_MAX; // no driver, much space } +/** +** Get used bytes in audio output. +*/ +static int NoopUsedBytes(void) +{ + return 0; // no driver, nothing used +} + /** ** Get audio delay in time stamps. ** @@ -1970,9 +2044,11 @@ static void NoopVoid(void) static const AudioModule NoopModule = { .Name = "noop", .Enqueue = NoopEnqueue, + .VideoReady = NoopVoid, .FlushBuffers = NoopVoid, .Poller = NoopVoid, .FreeBytes = NoopFreeBytes, + .UsedBytes = NoopUsedBytes, .GetDelay = NoopGetDelay, .SetVolume = NoopSetVolume, .Setup = NoopSetup, @@ -2002,9 +2078,10 @@ static void *AudioPlayHandlerThread(void *dummy) pthread_cond_wait(&AudioStartCond, &AudioMutex); // cond_wait can return, without signal! } while (!AudioRunning); - Debug(3, "audio/alsa: ----> %zd ms\n", - (RingBufferUsedBytes(AlsaRingBuffer) * 1000) + + Debug(3, "audio: ----> %d ms\n", (AudioUsedBytes() * 1000) / (AudioSampleRate * AudioChannels * AudioBytesProSample)); + pthread_mutex_unlock(&AudioMutex); #ifdef USE_AUDIORING @@ -2130,6 +2207,15 @@ void AudioEnqueue(const void *samples, int count) } } +/** +** Video is ready. +*/ +void AudioVideoReady(void) +{ + AudioVideoIsReady = 1; + AudioUsedModule->VideoReady(); +} + /** ** Flush audio buffers. */ @@ -2154,6 +2240,14 @@ int AudioFreeBytes(void) return AudioUsedModule->FreeBytes(); } +/** +** Get used bytes in audio output. +*/ +int AudioUsedBytes(void) +{ + return AudioUsedModule->UsedBytes(); +} + /** ** Get audio delay in time stamps. ** diff --git a/audio.h b/audio.h index 9163f72..94ac55a 100644 --- a/audio.h +++ b/audio.h @@ -31,8 +31,7 @@ extern void AudioEnqueue(const void *, int); ///< buffer audio samples extern void AudioFlushBuffers(void); ///< flush audio buffers extern void AudioPoller(void); ///< poll audio events/handling extern int AudioFreeBytes(void); ///< free bytes in audio output - -//extern int AudioUsedBytes(void); ///< used bytes in audio output +extern int AudioUsedBytes(void); ///< used bytes in audio output extern uint64_t AudioGetDelay(void); ///< get current audio delay extern void AudioSetClock(int64_t); ///< set audio clock base extern int64_t AudioGetClock(); ///< get current audio clock diff --git a/video.c b/video.c index 92d3b7e..434fc1e 100644 --- a/video.c +++ b/video.c @@ -331,7 +331,7 @@ int VideoAudioDelay; /// Default zoom mode static VideoZoomModes Video4to3ZoomMode; -//static char VideoSoftStartSync; ///< soft start sync audio/video +static char VideoSoftStartSync = 1; ///< soft start sync audio/video static char Video60HzMode; ///< handle 60hz displays @@ -340,6 +340,7 @@ static xcb_atom_t NetWmState; ///< wm-state message atom static xcb_atom_t NetWmStateFullscreen; ///< fullscreen wm-state message atom extern uint32_t VideoSwitch; ///< ticks for channel switch +extern void AudioVideoReady(void); ///< tell audio video is ready #ifdef USE_VIDEO_THREAD @@ -381,6 +382,9 @@ static const char *VideoTimeStampString(int64_t ts) int ss; int uu; + if (ts == (int64_t) AV_NOPTS_VALUE) { + return "--:--:--.---"; + } idx ^= 1; // support two static buffers ts = ts / 90; uu = ts % 1000; @@ -410,6 +414,7 @@ static void VideoSetPts(int64_t * pts_p, int interlaced, const AVFrame * frame) if (*pts_p != (int64_t) AV_NOPTS_VALUE) { *pts_p += interlaced ? 40 * 90 : 20 * 90; } + //av_opt_ptr(avcodec_get_frame_class(), frame, "best_effort_timestamp"); //pts = frame->best_effort_timestamp; pts = frame->pkt_pts; if (pts == (int64_t) AV_NOPTS_VALUE || !pts) { @@ -1342,6 +1347,7 @@ struct _vaapi_decoder_ struct timespec FrameTime; ///< time of last display int64_t PTS; ///< video PTS clock + int StartCounter; ///< number of start frames int FramesDuped; ///< number of frames duplicated int FramesMissed; ///< number of frames missed int FramesDropped; ///< number of frames dropped @@ -1813,10 +1819,13 @@ static void VaapiCleanup(VaapiDecoder * decoder) if (decoder->DeintImages[0].image_id != VA_INVALID_ID) { VaapiDestroyDeinterlaceImages(decoder); } + decoder->SurfaceRead = 0; + decoder->SurfaceWrite = 0; decoder->SurfaceField = 1; //decoder->FrameCounter = 0; + decoder->StartCounter = 0; decoder->PTS = AV_NOPTS_VALUE; VideoDeltaPTS = 0; } @@ -4379,6 +4388,14 @@ static void VaapiSyncDisplayFrame(VaapiDecoder * decoder) filled = atomic_read(&decoder->SurfacesFilled); // FIXME: audio not known assume 333ms delay + decoder->StartCounter++; + if (!VideoSoftStartSync && decoder->StartCounter < 60 + && (audio_clock == (int64_t) AV_NOPTS_VALUE + || video_clock > audio_clock + VideoAudioDelay + 120 * 90)) { + Debug(3, "video: initial slow down %d\n", decoder->StartCounter); + decoder->DupNextFrame = 2; + } + if (decoder->DupNextFrame) { decoder->DupNextFrame--; ++decoder->FramesDuped; @@ -4388,23 +4405,17 @@ static void VaapiSyncDisplayFrame(VaapiDecoder * decoder) if (abs(video_clock - audio_clock) > 5000 * 90) { Debug(3, "video: pts difference too big\n"); - } else if (video_clock > audio_clock + VideoAudioDelay + 80 * 90) { + } else if (video_clock > audio_clock + VideoAudioDelay + 100 * 90) { Debug(3, "video: slow down video\n"); decoder->DupNextFrame += 2; - } else if (video_clock > audio_clock + VideoAudioDelay + 30 * 90) { + } else if (video_clock > audio_clock + VideoAudioDelay + 45 * 90) { Debug(3, "video: slow down video\n"); decoder->DupNextFrame++; - } else if (audio_clock + VideoAudioDelay > video_clock + 40 * 90 + } else if (audio_clock + VideoAudioDelay > video_clock + 15 * 90 && filled > 1) { Debug(3, "video: speed up video\n"); decoder->DropNextFrame = 1; } - } else if (audio_clock == (int64_t) AV_NOPTS_VALUE - && video_clock != (int64_t) AV_NOPTS_VALUE) { - if (VideoGetBuffers() < 4) { - Debug(3, "video: initial slow down video\n"); - decoder->DupNextFrame++; - } } #if defined(DEBUG) || defined(AV_INFO) // debug audio/video sync @@ -4892,6 +4903,7 @@ typedef struct _vdpau_decoder_ struct timespec FrameTime; ///< time of last display int64_t PTS; ///< video PTS clock + int StartCounter; ///< number of start frames int FramesDuped; ///< number of frames duplicated int FramesMissed; ///< number of frames missed int FramesDropped; ///< number of frames dropped @@ -5568,6 +5580,8 @@ static void VdpauCleanup(VdpauDecoder * decoder) decoder->SurfaceField = 0; + //decoder->FrameCounter = 0; + decoder->StartCounter = 0; decoder->PTS = AV_NOPTS_VALUE; VideoDeltaPTS = 0; } @@ -7431,6 +7445,14 @@ static void VdpauSyncDisplayFrame(VdpauDecoder * decoder) filled = atomic_read(&decoder->SurfacesFilled); // FIXME: audio not known assume 333ms delay + decoder->StartCounter++; + if (!VideoSoftStartSync && decoder->StartCounter < 60 + && (audio_clock == (int64_t) AV_NOPTS_VALUE + || video_clock > audio_clock + VideoAudioDelay + 120 * 90)) { + Debug(3, "video: initial slow down %d\n", decoder->StartCounter); + decoder->DupNextFrame = 2; + } + if (decoder->DupNextFrame) { decoder->DupNextFrame--; ++decoder->FramesDuped; @@ -7440,23 +7462,17 @@ static void VdpauSyncDisplayFrame(VdpauDecoder * decoder) if (abs(video_clock - audio_clock) > 5000 * 90) { Debug(3, "video: pts difference too big\n"); - } else if (video_clock > audio_clock + VideoAudioDelay + 80 * 90) { + } else if (video_clock > audio_clock + VideoAudioDelay + 100 * 90) { Debug(3, "video: slow down video\n"); decoder->DupNextFrame += 2; - } else if (video_clock > audio_clock + VideoAudioDelay + 30 * 90) { + } else if (video_clock > audio_clock + VideoAudioDelay + 45 * 90) { Debug(3, "video: slow down video\n"); decoder->DupNextFrame++; - } else if (audio_clock + VideoAudioDelay > video_clock + 40 * 90 + } else if (audio_clock + VideoAudioDelay > video_clock + 15 * 90 && filled > 1 + 2 * decoder->Interlaced) { Debug(3, "video: speed up video\n"); decoder->DropNextFrame = 1; } - } else if (audio_clock == (int64_t) AV_NOPTS_VALUE - && video_clock != (int64_t) AV_NOPTS_VALUE) { - if (VideoGetBuffers() < 4) { - Debug(3, "video: initial slow down video\n"); - decoder->DupNextFrame++; - } } #if defined(DEBUG) || defined(AV_INFO) // debug audio/video sync @@ -7483,14 +7499,9 @@ static void VdpauSyncRenderFrame(VdpauDecoder * decoder, { VideoSetPts(&decoder->PTS, decoder->Interlaced, frame); - if (VdpauPreemption) { // display preempted - return; - } -#ifdef DEBUG if (!atomic_read(&decoder->SurfacesFilled)) { Debug(3, "video: new stream frame %d\n", GetMsTicks() - VideoSwitch); } -#endif if (decoder->DropNextFrame) { // drop frame requested ++decoder->FramesDropped; @@ -7502,6 +7513,9 @@ static void VdpauSyncRenderFrame(VdpauDecoder * decoder, decoder->DropNextFrame--; return; } + if (VdpauPreemption) { // display preempted + return; + } // if video output buffer is full, wait and display surface. // loop for interlace while (atomic_read(&decoder->SurfacesFilled) >= VIDEO_SURFACES_MAX) { @@ -8618,6 +8632,7 @@ void VideoReleaseSurface(VideoHwDecoder * decoder, unsigned surface) enum PixelFormat Video_get_format(VideoHwDecoder * decoder, AVCodecContext * video_ctx, const enum PixelFormat *fmt) { + AudioVideoReady(); if (VideoUsedModule) { return VideoUsedModule->get_format(decoder, video_ctx, fmt); }