summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohns <johns98@gmx.net>2012-01-08 21:46:00 +0100
committerJohns <johns98@gmx.net>2012-01-08 21:46:00 +0100
commitf6df79e8e64632314c72d85c2410141ab68bff1e (patch)
tree145e90a504d60f50e7e3b17c15b60096e235f2cb
parentf1551cd321bdadc69f95a450f2934ad71d3d4a8c (diff)
downloadvdr-plugin-softhddevice-f6df79e8e64632314c72d85c2410141ab68bff1e.tar.gz
vdr-plugin-softhddevice-f6df79e8e64632314c72d85c2410141ab68bff1e.tar.bz2
Improved replay of recordings.
-rw-r--r--Todo2
-rw-r--r--audio.c99
-rw-r--r--audio.h8
-rw-r--r--codec.c23
-rw-r--r--codec.h24
-rw-r--r--softhddev.c78
-rw-r--r--softhddev.h8
-rw-r--r--softhddevice.cpp46
-rw-r--r--video.c15
9 files changed, 244 insertions, 59 deletions
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);
@@ -1673,6 +1696,30 @@ void AudioEnqueue(const void *samples, int 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.
*/
void AudioPoller(void)
@@ -1688,6 +1735,20 @@ void AudioPoller(void)
}
/**
+** 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.
**
** @param pts audio presentation timestamp
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",