diff --git a/Todo b/Todo index 58f9033..68f1f30 100644 --- a/Todo +++ b/Todo @@ -83,8 +83,8 @@ HDMI/SPDIF Passthrough: Channels are wrong setup, if changing setting during operation. playback of recording - play back is too fast pause is not reset, when replay exit + replay/pause need 100% cpu setup: Setup of decoder type. diff --git a/audio.c b/audio.c index 4afb154..e778fd0 100644 --- a/audio.c +++ b/audio.c @@ -342,6 +342,11 @@ static int AlsaPlayRingbuffer(void) if (err == -EAGAIN) { goto again; } + /* + if (err == -EBADFD) { + goto again; + } + */ Error(_("audio/alsa: underrun error?\n")); err = snd_pcm_recover(AlsaPCMHandle, err, 0); if (err >= 0) { @@ -370,9 +375,13 @@ static void AlsaFlushBuffers(void) int err; RingBufferReadAdvance(AlsaRingBuffer, RingBufferUsedBytes(AlsaRingBuffer)); - if ((err = snd_pcm_drop(AlsaPCMHandle))) { + if ((err = snd_pcm_drop(AlsaPCMHandle)) < 0) { Error(_("audio: snd_pcm_drop(): %s\n"), snd_strerror(err)); } + if ((err = snd_pcm_prepare(AlsaPCMHandle)) < 0) { + Error(_("audio: snd_pcm_prepare(): %s\n"), snd_strerror(err)); + } + AudioPTS = INT64_C(0x8000000000000000); } #if 0 @@ -567,8 +576,20 @@ static void AlsaThread(void) for (;;) { int err; - Debug(4, "audio: play loop\n"); pthread_testcancel(); + if (AlsaFlushBuffer) { + // we can flush too many, but wo cares + Debug(3, "audio/alsa: flushing buffers\n"); + AlsaFlushBuffers(); + /* + if ((err = snd_pcm_prepare(AlsaPCMHandle))) { + Error(_("audio: snd_pcm_prepare(): %s\n"), snd_strerror(err)); + } + */ + AlsaFlushBuffer = 0; + break; + } + // wait for space in kernel buffers if ((err = snd_pcm_wait(AlsaPCMHandle, 100)) < 0) { Error(_("audio/alsa: wait underrun error?\n")); err = snd_pcm_recover(AlsaPCMHandle, err, 0); @@ -580,14 +601,7 @@ static void AlsaThread(void) continue; } if (AlsaFlushBuffer) { - // we can flush too many, but wo cares - Debug(3, "audio/alsa: flushing buffers\n"); - AlsaFlushBuffers(); - if ((err = snd_pcm_prepare(AlsaPCMHandle))) { - Error(_("audio: snd_pcm_prepare(): %s\n"), snd_strerror(err)); - } - AlsaFlushBuffer = 0; - break; + continue; } if ((err = AlsaPlayRingbuffer())) { // empty / error snd_pcm_state_t state; @@ -600,7 +614,8 @@ static void AlsaThread(void) Debug(3, "audio/alsa: stopping play\n"); break; } - usleep(20 * 1000); + pthread_yield(); + usleep(20 * 1000); // let fill the buffers } } } @@ -1195,6 +1210,21 @@ static int OssPlayRingbuffer(void) return 0; } +/** +** Flush oss buffers. +*/ +static void OssFlushBuffers(void) +{ + RingBufferReadAdvance(OssRingBuffer, RingBufferUsedBytes(OssRingBuffer)); + // flush kernel buffers + if (ioctl(OssPcmFildes, SNDCTL_DSP_HALT_OUTPUT, NULL) < 0) { + Error(_("audio/oss: ioctl(SNDCTL_DSP_HALT_OUTPUT): %s\n"), + strerror(errno)); + return; + } + AudioPTS = INT64_C(0x8000000000000000); +} + //---------------------------------------------------------------------------- // OSS pcm polled //---------------------------------------------------------------------------- @@ -1410,14 +1440,7 @@ static int OssSetup(int *freq, int *channels) // flush any buffered data { AudioRunning = 0; - RingBufferReadAdvance(OssRingBuffer, - RingBufferUsedBytes(OssRingBuffer)); - // flush kernel buffers - if (ioctl(OssPcmFildes, SNDCTL_DSP_HALT_OUTPUT, NULL) == -1) { - Error(_("audio/oss: ioctl(SNDCTL_DSP_HALT_OUTPUT): %s\n"), - strerror(errno)); - return -1; - } + OssFlushBuffers(); } AudioPTS = INT64_C(0x8000000000000000); @@ -1672,6 +1695,30 @@ void AudioEnqueue(const void *samples, int count) (void)count; } +/** +** Flush audio buffers. +*/ +void AudioFlushBuffers(void) +{ +#ifdef USE_ALSA +#ifdef USE_AUDIO_THREAD + if (AudioRunning) { + while (AudioRunning) { + AlsaFlushBuffer = 1; + usleep(1 * 1000); + } + AlsaFlushBuffer = 0; + } else +#endif + { + AlsaFlushBuffers(); + } +#endif +#ifdef USE_OSS + OssFlushBuffers(); +#endif +} + /** ** Call back to play audio polled. */ @@ -1687,6 +1734,20 @@ void AudioPoller(void) #endif } +/** +** Get free bytes in audio output. +*/ +int AudioFreeBytes(void) +{ +#ifdef USE_ALSA + return RingBufferFreeBytes(AlsaRingBuffer); +#endif +#ifdef USE_OSS + return RingBufferFreeBytes(OssRingBuffer); +#endif + return -1; +} + /** ** Set audio clock base. ** diff --git a/audio.h b/audio.h index fbd41ed..eec0163 100644 --- a/audio.h +++ b/audio.h @@ -1,7 +1,7 @@ /// /// @file audio.h @brief Audio module headerfile /// -/// Copyright (c) 2009 - 2011 by Johns. All Rights Reserved. +/// Copyright (c) 2009 - 2012 by Johns. All Rights Reserved. /// /// Contributor(s): /// @@ -28,12 +28,14 @@ //---------------------------------------------------------------------------- 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 AudioFreeBytes(void); ///< free bytes in audio output //extern int AudioUsedBytes(void); ///< used bytes in audio output extern void AudioSetClock(int64_t); ///< set audio clock base extern int64_t AudioGetClock(); ///< get current audio clock - extern uint64_t AudioGetDelay(void); ///< get current audio delay extern int AudioSetup(int *, int *); ///< setup audio output diff --git a/codec.c b/codec.c index 790f2e0..158b1eb 100644 --- a/codec.c +++ b/codec.c @@ -561,6 +561,16 @@ void CodecVideoDecode(VideoDecoder * decoder, const AVPacket * avpkt) } } +/** +** Flush the video decoder. +** +** @param decoder video decoder data +*/ +void CodecVideoFlushBuffers(VideoDecoder * decoder) +{ + avcodec_flush_buffers(decoder->VideoCtx); +} + //---------------------------------------------------------------------------- // Audio //---------------------------------------------------------------------------- @@ -738,9 +748,11 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt) spkt->pts = avpkt->pts; spkt->dts = avpkt->dts; #endif +#ifdef DEBUG if (!audio_decoder->AudioParser) { Fatal(_("codec: internal error parser freeded while running\n")); } +#endif audio_ctx = audio_decoder->AudioCtx; index = 0; @@ -1026,6 +1038,17 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt) #endif +/** +** Flush the audio decoder. +** +** @param decoder audio decoder data +*/ +void CodecAudioFlushBuffers(AudioDecoder * decoder) +{ + // FIXME: reset audio parser + avcodec_flush_buffers(decoder->AudioCtx); +} + //---------------------------------------------------------------------------- // Codec //---------------------------------------------------------------------------- diff --git a/codec.h b/codec.h index b171a1b..6656d6b 100644 --- a/codec.h +++ b/codec.h @@ -1,7 +1,7 @@ /// /// @file codec.h @brief Codec module headerfile /// -/// Copyright (c) 2009 - 2011 by Johns. All Rights Reserved. +/// Copyright (c) 2009 - 2012 by Johns. All Rights Reserved. /// /// Contributor(s): /// @@ -40,26 +40,32 @@ typedef struct _audio_decoder_ AudioDecoder; /// Allocate a new video decoder context. extern VideoDecoder *CodecVideoNewDecoder(VideoHwDecoder *); - /// Open video codec + /// Open video codec. extern void CodecVideoOpen(VideoDecoder *, const char *, int); - /// Close video codec + /// Close video codec. extern void CodecVideoClose(VideoDecoder *); - /// Decode a video packet -extern void CodecVideoDecode(VideoDecoder *, const AVPacket * pkt); + /// Decode a video packet. +extern void CodecVideoDecode(VideoDecoder *, const AVPacket *); + + /// Flush video buffers. +extern void CodecVideoFlushBuffers(VideoDecoder *); /// Allocate a new audio decoder context. extern AudioDecoder *CodecAudioNewDecoder(void); - /// Open audio codec + /// Open audio codec. extern void CodecAudioOpen(AudioDecoder *, const char *, int); - /// Close audio codec + /// Close audio codec. extern void CodecAudioClose(AudioDecoder *); - /// Decode an audio packet -extern void CodecAudioDecode(AudioDecoder *, const AVPacket * pkt); + /// Decode an audio packet. +extern void CodecAudioDecode(AudioDecoder *, const AVPacket *); + + /// Flush audio buffers. +extern void CodecAudioFlushBuffers(AudioDecoder *); /// Setup and initialize codec module. extern void CodecInit(void); diff --git a/softhddev.c b/softhddev.c index 1a8e27a..a715886 100644 --- a/softhddev.c +++ b/softhddev.c @@ -59,6 +59,7 @@ static const char DeviceStopped = 1; ///< flag device stopped ////////////////////////////////////////////////////////////////////////////// static volatile char NewAudioStream; ///< new audio stream +static volatile char SkipAudio; ///< skip audio stream static AudioDecoder *MyAudioDecoder; ///< audio decoder static enum CodecID AudioCodecID; ///< current codec id @@ -185,10 +186,11 @@ static int FindAudioSync(const AVPacket * avpkt) ** @param size size of PES packet ** @param id PES packet type */ -void PlayAudio(const uint8_t * data, int size, +int PlayAudio(const uint8_t * data, int size, __attribute__ ((unused)) uint8_t id) { int n; + int osize; AVPacket avpkt[1]; // channel switch: SetAudioChannelDevice: SetDigitalAudioDevice: @@ -199,12 +201,19 @@ void PlayAudio(const uint8_t * data, int size, AudioCodecID = CODEC_ID_NONE; NewAudioStream = 0; } + if (SkipAudio) { + return size; + } // PES header 0x00 0x00 0x01 ID // ID 0xBD 0xC0-0xCF if (size < 9) { Error(_("[softhddev] invalid audio packet\n")); - return; + return size; + } + // Don't overrun audio buffers on replay + if (AudioFreeBytes() < 3072 * 8 * 8) { // 8 channels 8 packets + return 0; } n = data[8]; // header size @@ -225,11 +234,12 @@ void PlayAudio(const uint8_t * data, int size, } } + osize = size; data += 9 + n; size -= 9 + n; // skip pes header if (size <= 0) { Error(_("[softhddev] invalid audio packet\n")); - return; + return osize; } // Detect audio code // MPEG-PS mp2 MPEG1, MPEG2, AC3 @@ -271,7 +281,7 @@ void PlayAudio(const uint8_t * data, int size, avpkt->size = size; n = FindAudioSync(avpkt); if (n < 0) { - return; + return osize; } if (!MyAudioDecoder) { MyAudioDecoder = CodecAudioNewDecoder(); @@ -286,13 +296,15 @@ void PlayAudio(const uint8_t * data, int size, // no decoder or codec known if (!MyAudioDecoder || AudioCodecID == CODEC_ID_NONE) { - return; + return osize; } avpkt->data = (void *)data; avpkt->size = size; //memset(avpkt->data + avpkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE); CodecAudioDecode(MyAudioDecoder, avpkt); + + return osize; } /** @@ -300,6 +312,7 @@ void PlayAudio(const uint8_t * data, int size, */ void Mute(void) { + SkipAudio = 1; AudioSetVolume(0); } @@ -480,6 +493,10 @@ int VideoDecode(void) } if (VideoClearBuffers) { atomic_set(&VideoPacketsFilled, 0); + VideoPacketRead = VideoPacketWrite; + if (MyVideoDecoder) { + CodecVideoFlushBuffers(MyVideoDecoder); + } VideoClearBuffers = 0; return 1; } @@ -751,8 +768,12 @@ void SetPlayMode(void) } } if (MyAudioDecoder) { - NewAudioStream = 1; + if (AudioCodecID != CODEC_ID_NONE) { + NewAudioStream = 1; + } } + VideoFreezed = 0; + SkipAudio = 0; } /** @@ -760,9 +781,16 @@ void SetPlayMode(void) */ void Clear(void) { + int i; + + VideoNextPacket(VideoCodecID); // terminate work VideoClearBuffers = 1; // FIXME: avcodec_flush_buffers - // FIXME: flush audio buffers + AudioFlushBuffers(); + + for (i = 0; VideoClearBuffers && i < 20; ++i) { + usleep(1 * 1000); + } } /** @@ -771,6 +799,7 @@ void Clear(void) void Play(void) { VideoFreezed = 0; + SkipAudio = 0; // FIXME: restart audio } @@ -781,16 +810,33 @@ void Freeze(void) { VideoFreezed = 1; // FIXME: freeze audio + AudioFlushBuffers(); +} + +/** +** Display the given I-frame as a still picture. +*/ +void StillPicture(const uint8_t * data, int size) +{ + // must be a PES start code + if (size < 9 || !data || data[0] || data[1] || data[2] != 0x01) { + Error(_("[softhddev] invalid PES video packet\n")); + return; + } + PlayVideo(data, size); + PlayVideo(data, size); + VideoNextPacket(VideoCodecID); // terminate work } /** ** Poll if device is ready. Called by replay. +** +** @param timeout timeout to become ready in ms */ int Poll(int timeout) { // buffers are too full if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX / 2) { - Debug(3, "replay: poll %d\n", timeout); if (timeout) { // let display thread work usleep(timeout * 1000); @@ -800,6 +846,22 @@ int Poll(int timeout) return 0; } +/** +** Flush the device output buffers. +** +** @param timeout timeout to flush in ms +*/ +int Flush(int timeout) +{ + if (atomic_read(&VideoPacketsFilled)) { + if (timeout) { // let display thread work + usleep(timeout * 1000); + } + return !atomic_read(&VideoPacketsFilled); + } + return 1; +} + ////////////////////////////////////////////////////////////////////////////// // OSD ////////////////////////////////////////////////////////////////////////////// diff --git a/softhddev.h b/softhddev.h index c54fd2e..fbd4673 100644 --- a/softhddev.h +++ b/softhddev.h @@ -1,7 +1,7 @@ /// /// @file softhddev.h @brief software HD device plugin header file. /// -/// Copyright (c) 2011 by Johns. All Rights Reserved. +/// Copyright (c) 2011 - 2012 by Johns. All Rights Reserved. /// /// Contributor(s): /// @@ -36,7 +36,7 @@ extern "C" extern void OsdDrawARGB(int, int, int, int, const uint8_t *); /// C plugin play audio packet - extern void PlayAudio(const uint8_t *, int, uint8_t); + extern int PlayAudio(const uint8_t *, int, uint8_t); /// C plugin mute audio extern void Mute(void); /// C plugin set audio volume @@ -55,8 +55,12 @@ extern "C" extern void Play(void); /// C plugin sets the device into "freeze frame" mode extern void Freeze(void); + /// C plugin display I-frame as a still picture. + extern void StillPicture(const uint8_t *, int); /// C plugin poll if ready extern int Poll(int); + /// C plugin flush output buffers + extern int Flush(int); /// C plugin command line help extern const char *CommandLineHelp(void); diff --git a/softhddevice.cpp b/softhddevice.cpp index 19f2c9f..5b94383 100644 --- a/softhddevice.cpp +++ b/softhddevice.cpp @@ -499,9 +499,14 @@ int64_t cSoftHdDevice::GetSTC(void) return::VideoGetClock(); } -void cSoftHdDevice::TrickSpeed(int Speed) +/** +** Set trick play speed. +** +** @param speed trick speed +*/ +void cSoftHdDevice::TrickSpeed(int speed) { - dsyslog("[softhddev]%s: %d\n", __FUNCTION__, Speed); + dsyslog("[softhddev]%s: %d\n", __FUNCTION__, speed); } void cSoftHdDevice::Clear(void) @@ -533,24 +538,37 @@ void cSoftHdDevice::Mute(void) dsyslog("[softhddev]%s:\n", __FUNCTION__); cDevice::Mute(); - ::Mute(); } void cSoftHdDevice::SetVolumeDevice(int volume) { - //dsyslog("[softhddev]%s: %d\n", __FUNCTION__, volume); + dsyslog("[softhddev]%s: %d\n", __FUNCTION__, volume); ::SetVolumeDevice(volume); } -void cSoftHdDevice::StillPicture( - __attribute__ ((unused)) const uchar * data, __attribute__ ((unused)) - int length) +/** +** Display the given I-frame as a still picture. +*/ +void cSoftHdDevice::StillPicture(const uchar * data, int length) { dsyslog("[softhddev]%s:\n", __FUNCTION__); + + if ( data[0] == 0x47 ) { // ts sync + cDevice::StillPicture(data, length); + return; + } + + ::StillPicture(data, length); } +/** +** Check if the device is ready for further action. +** +** @param poller file handles (unused) +** @param timeout_ms timeout in ms to become ready +*/ bool cSoftHdDevice::Poll( __attribute__ ((unused)) cPoller & poller, int timeout_ms) { @@ -559,11 +577,16 @@ bool cSoftHdDevice::Poll( return::Poll(timeout_ms); } +/** +** Flush the device output buffers. +** +** @param timeout_ms timeout in ms to become ready +*/ bool cSoftHdDevice::Flush(int timeout_ms) { dsyslog("[softhddev]%s: %d ms\n", __FUNCTION__, timeout_ms); - return true; + return::Flush(timeout_ms); } // ---------------------------------------------------------------------------- @@ -580,13 +603,14 @@ void cSoftHdDevice::GetOsdSize(int &width, int &height, double &pixel_aspect) // ---------------------------------------------------------------------------- +/** +** Play a audio packet. +*/ int cSoftHdDevice::PlayAudio(const uchar * data, int length, uchar id) { //dsyslog("[softhddev]%s: %p %p %d %d\n", __FUNCTION__, this, data, length, id); - ::PlayAudio(data, length, id); - - return length; + return::PlayAudio(data, length, id); } void cSoftHdDevice::SetAudioTrackDevice( diff --git a/video.c b/video.c index ce80a7a..4bf7717 100644 --- a/video.c +++ b/video.c @@ -6245,6 +6245,7 @@ enum PixelFormat Video_get_format(VideoHwDecoder * decoder, static void VideoSetPts(int64_t * pts_p, int interlaced, const AVFrame * frame) { int64_t pts; + int64_t delta; // update video clock if ((uint64_t) * pts_p != AV_NOPTS_VALUE) { @@ -6259,14 +6260,16 @@ static void VideoSetPts(int64_t * pts_p, int interlaced, const AVFrame * frame) if (!pts) { pts = AV_NOPTS_VALUE; } - // build a monotonic pts - if ((uint64_t) * pts_p != AV_NOPTS_VALUE) { - if (pts - *pts_p < -10 * 90) { - pts = AV_NOPTS_VALUE; - } - } // libav: sets only pkt_dts which can be 0 if ((uint64_t) pts != AV_NOPTS_VALUE) { + // build a monotonic pts + if ((uint64_t) * pts_p != AV_NOPTS_VALUE) { + delta = pts - *pts_p; + // ignore negative jumps + if (delta > -300 * 90 && delta < -15 * 90) { + return; + } + } if (*pts_p != pts) { Debug(3, "video: %#012" PRIx64 "->%#012" PRIx64 " %4" PRId64 " pts\n",