44 Commits
0.4.9 ... 0.5.0

Author SHA1 Message Date
Johns
e10e62dcf7 Release Version 0.5.0. 2012-04-07 20:21:55 +02:00
Johns
2a1793c98e Use config value to change audio/video delay. 2012-04-07 13:43:51 +02:00
Johns
30d4586448 Change audio/video delay with hot-key. 2012-04-07 00:15:57 +02:00
Johns
aa4debc9c8 Enable/disable/toggle fullscreen with hot-key. 2012-04-06 14:38:51 +02:00
ac2e10a308 Cutting pixels configured for each resolution. 2012-04-05 22:51:20 +02:00
Johns
c986d285ea Buffer less video and audio. 2012-04-05 15:47:59 +02:00
Johns
8612044b9b Move suspend on inactivity to houesekeeping. 2012-04-05 15:43:32 +02:00
Johns
c19b86411a Update documents. 2012-04-05 15:42:48 +02:00
Johns
9165052d5e Fix gcc error bug (also for VA-API). 2012-04-03 16:36:06 +02:00
Johns
413983a666 Calling TrickSpeed without decoder can happen. 2012-04-01 17:04:16 +02:00
Johns
f86fa4edd7 VDR 1.7.27 suggested change. 2012-04-01 17:02:15 +02:00
Johns
7f8110557f Fix gcc error bug. 2012-03-31 23:20:06 +02:00
Johns
c9b344a3fd Audio/Video sync rewrite.
Trick-speed support moved to video module.
Reduce video messages.
2012-03-31 21:27:54 +02:00
Johns
b41f934c37 Faster VdpauBlackSurface version. 2012-03-30 17:19:31 +02:00
Johns
6058f3da56 Fix bug: VideoSetPts wrong position. 2012-03-30 16:04:25 +02:00
689d75b808 Add VideoSkipPixels support. 2012-03-26 20:49:18 +02:00
Johns
bd4503f30b More debug for flush buffers. Bigger audio buffer. 2012-03-23 18:43:20 +01:00
Johns
24ba8175a3 Disable suspend on inactivity until player fixed. 2012-03-22 16:06:32 +01:00
Johns
fe24cbb182 mp3 needs 100% cpu again! 2012-03-20 16:36:42 +01:00
Johns
6eff8fa818 Forgot VDPAU in requires. 2012-03-19 17:15:21 +01:00
Johns
552a994db3 Add optional argument to ATTA svdrp commmand. 2012-03-15 15:42:51 +01:00
Johns
d24f19bc2d More SVDRP commands help. 2012-03-14 15:07:08 +01:00
Johns
7b570c507c Cleanups. 2012-03-12 17:58:19 +01:00
Johns
09ba3e2993 Let inactivity suspend wakeup with remote keys. 2012-03-11 14:12:49 +01:00
Johns
d0f825f831 Comments added. 2012-03-10 17:46:00 +01:00
Johns
47d2896468 Better Poll(), flush video buffers after replay. 2012-03-10 17:05:41 +01:00
Johns
f59425ac57 AudioGetDelay returns signed value and cleanups. 2012-03-10 15:00:58 +01:00
Johns
1acdeee913 Adds ffmpeg 0.8.7 bug workaround:
Single nal end seq aren't consumed and an endless loop entered.
2012-03-09 21:47:06 +01:00
Johns
c2938c7ef3 Wakeup display to show OSD for remote learning. 2012-03-09 12:08:56 +01:00
Johns
d65fe88c83 Support switching the primary device with svdrp. 2012-03-08 15:28:10 +01:00
Johns
7d3f4f4434 Disable and reenable screen saver and DPMS. 2012-03-08 15:25:10 +01:00
Johns
acc35fe30c Video cleanup.
Add noop video output module.
Move VideoThread check into lock/unlock functions.
Add support for choosing video output module.
2012-03-07 15:31:43 +01:00
Johns
ee5804fed7 Handle snd_pcm_wait timeouts. 2012-03-07 15:13:07 +01:00
Johns
1cbaddf75c Need extra space in ring buffer for sequence end. 2012-03-06 18:37:40 +01:00
Johns
226760490b VADisplayAttribDirectSurface removed. 2012-03-06 16:56:26 +01:00
Johns
7931909e28 Workaround should be for abs. 2012-03-06 15:39:29 +01:00
Johns
129c139ed7 Fix fast backward with some h264 streams. 2012-03-06 15:38:30 +01:00
Johns
340816d763 Make soft start sync setup menu configurable. 2012-03-06 12:16:47 +01:00
Johns
d6c6818ecf Workaround for av_resample_compensate ffmpeg bug.
FFmpeg commit a67cb012e6947fb238193afc0f18114f6e20818c or
1b9ca38d9d06d319fffd61d27e4eb385d6572ba8 breaks av_resample_compensate.
Only big sample_delta compensation_distance ratios are now working.
2012-03-05 20:38:43 +01:00
Johns
181a0bb372 Move grab unsupported warning to low-level. 2012-03-05 20:10:23 +01:00
Johns
f2d4163899 Fix bug: NAL end of sequence is 10 and not 0x10.
Cleanup, remove old cruft.
Add support for pes recordings.
2012-03-05 20:05:56 +01:00
Johns
4cc98d7937 Move time-stamp printing to misc.h. 2012-03-05 17:34:10 +01:00
Johns
3812fa8d38 Fix bug: AudioEnqueue crash without sound card. 2012-03-05 15:06:46 +01:00
Johns
da5c5cd5fd Version 0.4.9 released. 2012-03-04 22:36:14 +01:00
13 changed files with 1966 additions and 1073 deletions

View File

@@ -1,6 +1,46 @@
User johns
Date:
Date: Sat Apr 7 20:21:16 CEST 2012
Release Version 0.5.0
Change audio/video delay with hot-key.
Enable/disable/toggle fullscreen with hot-key (Feature #930).
User: CafeDelMar
Date: Thu Apr 5 22:44:06 CEST 2012
Cutting pixels are now configured for each resolution.
User johns
Date: Thu Apr 5 15:47:59 CEST 2012
Buffer less video and audio.
Fix 100% cpu use, with mp3 plugin.
Audio/Video sync rewrite, trick-speed support moved to video.
Faster VdpauBlackSurface version.
Fix bug: VideoSetPts wrong position for multi frame packets.
User: CafeDelMar
Date: Mon Mar 26 20:45:54 CEST 2012
Add VideoSkipPixels support.
User johns
Date: Fri Mar 23 18:43:20 CET 2012
Add optional argument (display) to ATTA svdrp commmand.
Wakeup display to show OSD for remote learning mode.
Support switching the primary device with svdrp.
Disable and reenable screen saver and DPMS.
Video source code cleanup.
Fix fast backward with some h264 streams.
Make soft start sync setup menu configurable.
Fix bug: StillPicture NAL end of sequence is 10 and not 0x10.
Fix bug: AudioEnqueue crash without sound card.
User johns
Date: Sun Mar 4 22:35:36 CET 2012
Release Version 0.4.9
Experimental ac3 audio drift correction support.
Removes LPCM detection from TS parser.
Rewrote video/audio start code.

View File

@@ -37,7 +37,7 @@ CXX ?= g++
CFLAGS ?= -g -O2 -W -Wall -Wextra -Winit-self \
-Wdeclaration-after-statement \
-ftree-vectorize -msse3 -flax-vector-conversions
CXXFLAGS ?= -g -O2 -W -Wall -Wextra -Woverloaded-virtual
CXXFLAGS ?= -g -O2 -W -Wall -Wextra -Werror=overloaded-virtual
### The directory environment:

View File

@@ -20,23 +20,24 @@ $Id$
A software and GPU emulated HD output device plugin for VDR.
o Video VA-API/VA-API (with intel, nvidia and amd backend supported)
o Video CPU/VA-API
o Video VDPAU/VDPAU
o Video CPU/VDPAU
o Audio FFMpeg/Alsa/Analog
o Audio FFMpeg/Alsa/Digital
o Audio FFMpeg/OSS/Analog
o HDMI/SPDIF Passthrough
o VA-API bob software deinterlace
o Auto-crop
o Video decoder CPU / VA-API / VDPAU
o Video output VA-API / VDPAU
o Audio FFMpeg / Alsa / Analog
o Audio FFMpeg / Alsa / Digital
o Audio FFMpeg / OSS / Analog
o HDMI/SPDIF pass-through
o YaepgHD support
o Software deinterlacer Bob (VA-API only)
o Autocrop
o Grab image (VDPAU only)
o Suspend
o Letterbox, Stretch and Center cut-out video display modes
o planned: Video VA-API/Opengl
o planned: Video VDPAU/Opengl
o planned: Video CPU/Xv
o planned: Video CPU/Opengl
o planned: Video decoder VA-API Branch: vaapi-ext/staging
o planned: Video output XvBA / Opengl / Xv
o planned: VA-API grab image
o planned: Improved Software Deinterlacer (yadif or/and ffmpeg filters)
o planned: Video XvBA/XvBA
o planned: software volume, software channel resample
o planned: atmo light support
To compile you must have the 'requires' installed.
@@ -137,6 +138,12 @@ Setup: /etc/vdr/setup.conf
-1000 .. 1000 noise reduction level (0 off, -1000 max blur,
1000 max sharp)
softhddevice.<res>.CutTopBottom = 0
Cut 'n' pixels at at top and bottom of the video picture.
softhddevice.<res>.CutLeftRight = 0
Cut 'n' pixels at at left and right of the video picture.
softhddevice.AudioDelay = 0
+n or -n ms
delay audio or delay video
@@ -166,10 +173,9 @@ Setup: /etc/vdr/setup.conf
32bit RGBA background color
(Red * 16777216 + Green * 65536 + Blue * 256 + Alpha)
or hex RRGGBBAA
grey = 2155905279
softhddevice.SkipLines = 0
skip 'n' lines at top and bottom of the video picture.
grey 127 * 16777216 + 127 * 65536 + 127 * 256 => 2139062016
in the setup menu this is entered as (24bit RGB and 8bit Alpha)
(Red * 65536 + Green * 256 + Blue)
softhddevice.StudioLevels = 0
0 use PC levels (0-255) with vdpau.
@@ -186,6 +192,10 @@ Setup: /etc/vdr/setup.conf
0 disable 60Hz display mode
1 enable 60Hz display mode
softhddevice.SoftStartSync = 0
0 disable soft start of audio/video sync
1 enable soft start of audio/video sync
VideoDisplayFormat = ?
0 pan and scan
1 letter box
@@ -275,6 +285,11 @@ Requires:
x11-libs/xvba-video
XVBA Backend for Video Acceleration (VA) API
http://www.freedesktop.org/wiki/Software/vaapi
x11-libs/libvdpau
VDPAU wrapper and trace libraries
http://www.freedesktop.org/wiki/Software/VDPAU
x11-libs/libxcb,
X C-language Bindings library
http://xcb.freedesktop.org

23
Todo
View File

@@ -19,28 +19,24 @@ GNU Affero General Public License for more details.
$Id: $
missing:
software deinterlace (yadif, ...)
software decoder with software deinterlace
more software deinterlace (yadif, ...)
more software decoder with software deinterlace
suspend output / energie saver: stop and restart X11
suspend plugin didn't restore full-screen (is this wanted?)
Option deinterlace off / deinterlace force!
ColorSpace aren't configurable with the gui.
Replay of old vdr 1.6 recordings.
svdrp support for hot-keys.
crash:
AudioPlayHandlerThread -> pthread_cond_wait
works for me: restart vdr not working, when started x11 was killed.
video:
subtitle not cleared
subtitle could be asyncron
reduce warnings after channel switch
grab image with hardware and better scaling support
hard channel switch
yaepghd changed position is lost on channel switch
pause (live tv) has sometime problems with SAT1 HD Pro7 HD
radio show black background
radio no need to wait on video buffers
starting with radio and own X11 server, shows no video
some low-bandwidth tv channels have hiccups.
vdpau:
software decoder path not working
@@ -54,8 +50,8 @@ libva:
[drm:i915_hangcheck_elapsed] *ERROR* Hangcheck timer elapsed... GPU hung
[drm:i915_wait_request] *ERROR* i915_wait_request returns -11 ...
libva: branch vaapi-ext
add support for vaapi-ext
libva: branch vaapi-ext / staging
add support for vaapi-ext / staging
libva-intel-driver:
deinterlace only supported with vaapi-ext
@@ -73,7 +69,6 @@ libva-vdpau-driver:
libva-xvba-driver:
x11:
disable screensaver
skip multiple configure-notify, handle only the last one.
support embedded mode
@@ -88,6 +83,7 @@ audio:
samplerate problem resume/suspend.
only wait for video start, if video is running.
Not primary device, don't use and block audio/video.
multiple open of audio device, reduce them.
audio/alsa:
better downmix of >2 channels on 2 channel hardware
@@ -108,7 +104,7 @@ playback of recording
replay/pause need 100% cpu (fixed?)
plugins:
mp3 plugin needs 100% cpu (OSD updates?)
mp3 plugin needs 100% cpu (bad ::Poll)
setup:
Setup of decoder type.
@@ -120,6 +116,7 @@ setup:
unsorted:
stoping vdr while plugin is suspended opens and closes a window.
svdrp prim: support plugin names for device numbers.
future features (not planed for 1.0 - 1.5)

149
audio.c
View File

@@ -105,20 +105,20 @@ typedef struct _audio_module_
{
const char *Name; ///< audio output module name
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
void (*Play) (void); ///< play
void (*Pause) (void); ///< pause
void (*Init) (void); ///< initialize audio output module
void (*Exit) (void); ///< cleanup audio output module
void (*const Thread) (void); ///< module thread handler
void (*const Enqueue) (const void *, int); ///< enqueue samples for output
void (*const VideoReady) (void); ///< video ready, start audio
void (*const FlushBuffers) (void); ///< flush sample buffers
void (*const Poller) (void); ///< output poller
int (*const FreeBytes) (void); ///< number of bytes free in buffer
int (*const UsedBytes) (void); ///< number of bytes used in buffer
int64_t(*const GetDelay) (void); ///< get current audio delay
void (*const SetVolume) (int); ///< set output volume
int (*const Setup) (int *, int *, int); ///< setup channels, samplerate
void (*const Play) (void); ///< play
void (*const Pause) (void); ///< pause
void (*const Init) (void); ///< initialize audio output module
void (*const Exit) (void); ///< cleanup audio output module
} AudioModule;
static const AudioModule NoopModule; ///< forward definition of noop module
@@ -223,7 +223,7 @@ static void AudioRingInit(void)
for (i = 0; i < AUDIO_RING_MAX; ++i) {
// FIXME:
//AlsaRingBuffer = RingBufferNew(48000 * 8 * 2); // ~1s 8ch 16bit
//AlsaRingBuffer = RingBufferNew(2 * 48000 * 8 * 2); // ~2s 8ch 16bit
}
// one slot always reservered
AudioRingWrite = 1;
@@ -432,11 +432,26 @@ static void AlsaFlushBuffers(void)
snd_pcm_state_t state;
if (AlsaRingBuffer && AlsaPCMHandle) {
#ifdef DEBUG
const void *r;
void *w;
#endif
RingBufferReadAdvance(AlsaRingBuffer,
RingBufferUsedBytes(AlsaRingBuffer));
#ifdef DEBUG
RingBufferGetWritePointer(AlsaRingBuffer, &w);
RingBufferGetReadPointer(AlsaRingBuffer, &r);
if (r != w) {
Fatal(_("audio/alsa: ringbuffer out of sync %zd-%zd\n"),
RingBufferGetWritePointer(AlsaRingBuffer, &w),
RingBufferGetReadPointer(AlsaRingBuffer, &r));
abort();
}
#endif
state = snd_pcm_state(AlsaPCMHandle);
Debug(3, "audio/alsa: flush state %d - %s\n", state,
snd_pcm_state_name(state));
Debug(3, "audio/alsa: flush state %s\n", snd_pcm_state_name(state));
if (state != SND_PCM_STATE_OPEN) {
if ((err = snd_pcm_drop(AlsaPCMHandle)) < 0) {
Error(_("audio: snd_pcm_drop(): %s\n"), snd_strerror(err));
@@ -672,7 +687,8 @@ static void AlsaThread(void)
usleep(24 * 1000);
continue;
}
if (AlsaFlushBuffer || AudioPaused) {
// timeout or some commands
if (!err || AlsaFlushBuffer || AudioPaused) {
continue;
}
if ((err = AlsaPlayRingbuffer())) { // empty / error
@@ -701,7 +717,7 @@ static void AlsaThread(void)
*/
static void AlsaThreadEnqueue(const void *samples, int count)
{
if (!AlsaRingBuffer || !AlsaPCMHandle || !AudioSampleRate) {
if (!AlsaRingBuffer || !AlsaPCMHandle) {
Debug(3, "audio/alsa: enqueue not ready\n");
return;
}
@@ -722,19 +738,31 @@ static void AlsaThreadEnqueue(const void *samples, int count)
*/
static void AlsaVideoReady(void)
{
if (AudioSampleRate && AudioChannels) {
Debug(3, "audio/alsa: start %4zdms video start\n",
(RingBufferUsedBytes(AlsaRingBuffer) * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
}
if (!AudioRunning) {
size_t used;
used = RingBufferUsedBytes(AlsaRingBuffer);
// enough video + audio buffered
if (AlsaStartThreshold < RingBufferUsedBytes(AlsaRingBuffer)) {
if (AlsaStartThreshold < used) {
// too much audio buffered, skip it
if (AlsaStartThreshold * 2 < used) {
Debug(3, "audio/alsa: start %4zdms skip ready\n",
((used - AlsaStartThreshold * 2) * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
RingBufferReadAdvance(AlsaRingBuffer,
used - AlsaStartThreshold * 2);
}
AudioRunning = 1;
pthread_cond_signal(&AudioStartCond);
}
}
if (AudioSampleRate && AudioChannels) {
Debug(3, "audio/alsa: start %4zdms video ready\n",
(RingBufferUsedBytes(AlsaRingBuffer) * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
}
}
/**
@@ -911,17 +939,17 @@ static void AlsaInitMixer(void)
**
** @todo FIXME: handle the case no audio running
*/
static uint64_t AlsaGetDelay(void)
static int64_t AlsaGetDelay(void)
{
int err;
snd_pcm_sframes_t delay;
uint64_t pts;
int64_t pts;
if (!AlsaPCMHandle || !AudioSampleRate) {
return 0UL;
return 0L;
}
if (!AudioRunning) { // audio not running
return 0UL;
return 0L;
}
// FIXME: thread safe? __assert_fail_base in snd_pcm_delay
@@ -939,10 +967,10 @@ static uint64_t AlsaGetDelay(void)
delay = 0L;
}
pts = ((uint64_t) delay * 90 * 1000) / AudioSampleRate;
pts += ((uint64_t) RingBufferUsedBytes(AlsaRingBuffer) * 90 * 1000)
pts = ((int64_t) delay * 90 * 1000) / AudioSampleRate;
pts += ((int64_t) RingBufferUsedBytes(AlsaRingBuffer) * 90 * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample);
Debug(4, "audio/alsa: hw+sw delay %zd %" PRId64 " ms\n",
Debug(4, "audio/alsa: hw+sw delay %zd %" PRId64 "ms\n",
RingBufferUsedBytes(AlsaRingBuffer), pts / 90);
return pts;
@@ -976,6 +1004,9 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
#if 1 // easy alsa hw setup way
// flush any buffered data
AudioFlushBuffers();
Debug(3, "audio: %dms flush\n", (AudioUsedBytes() * 1000)
/ (!AudioSampleRate + !AudioChannels +
AudioSampleRate * AudioChannels * AudioBytesProSample));
if (1) { // close+open to fix hdmi no sound bugs
handle = AlsaPCMHandle;
@@ -1146,7 +1177,7 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
// update buffer
snd_pcm_get_params(AlsaPCMHandle, &buffer_size, &period_size);
Info(_("audio/alsa: buffer size %lu %zdms, period size %lu %zdms\n"),
Debug(3, "audio/alsa: buffer size %lu %zdms, period size %lu %zdms\n",
buffer_size, snd_pcm_frames_to_bytes(AlsaPCMHandle,
buffer_size) * 1000 / (AudioSampleRate * AudioChannels *
AudioBytesProSample), period_size,
@@ -1171,7 +1202,7 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
if (AlsaStartThreshold > RingBufferFreeBytes(AlsaRingBuffer)) {
AlsaStartThreshold = RingBufferFreeBytes(AlsaRingBuffer);
}
Info(_("audio/alsa: delay %u ms\n"), (AlsaStartThreshold * 1000)
Info(_("audio/alsa: delay %ums\n"), (AlsaStartThreshold * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
return ret;
@@ -1241,7 +1272,7 @@ static void AlsaInit(void)
#else
(void)AlsaNoopCallback;
#endif
AlsaRingBuffer = RingBufferNew(48000 * 8 * 2); // ~1s 8ch 16bit
AlsaRingBuffer = RingBufferNew(2 * 48000 * 8 * 2); // ~2s 8ch 16bit
AlsaInitPCM();
AlsaInitMixer();
@@ -1449,7 +1480,7 @@ static void OssEnqueue(const void *samples, int count)
uint32_t tick;
tick = GetMsTicks();
Debug(4, "audio/oss: %4d %d ms\n", count, tick - last_tick);
Debug(4, "audio/oss: %4d %dms\n", count, tick - last_tick);
last_tick = tick;
#endif
@@ -1552,7 +1583,7 @@ static void OssThread(void)
*/
static void OssThreadEnqueue(const void *samples, int count)
{
if (!OssRingBuffer || OssPcmFildes == -1 || !AudioSampleRate) {
if (!OssRingBuffer || OssPcmFildes == -1) {
Debug(3, "audio/oss: enqueue not ready\n");
return;
}
@@ -1731,16 +1762,16 @@ static void OssInitMixer(void)
**
** @returns audio delay in time stamps.
*/
static uint64_t OssGetDelay(void)
static int64_t OssGetDelay(void)
{
int delay;
uint64_t pts;
int64_t pts;
if (OssPcmFildes == -1) { // setup failure
return 0UL;
return 0L;
}
if (!AudioRunning) { // audio not running
return 0UL;
return 0L;
}
// delay in bytes in kernel buffers
delay = -1;
@@ -1753,9 +1784,9 @@ static uint64_t OssGetDelay(void)
delay = 0;
}
pts = ((uint64_t) (delay + RingBufferUsedBytes(OssRingBuffer)) * 90 * 1000)
pts = ((int64_t) (delay + RingBufferUsedBytes(OssRingBuffer)) * 90 * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample);
Debug(4, "audio/oss: hw+sw delay %zd %" PRId64 " ms\n",
Debug(4, "audio/oss: hw+sw delay %zd %" PRId64 "ms\n",
RingBufferUsedBytes(OssRingBuffer), pts / 90);
return pts;
@@ -1864,7 +1895,7 @@ static int OssSetup(int *freq, int *channels, int use_ac3)
OssFragmentTime = (bi.fragsize * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample);
Info(_("audio/oss: buffer size %d %dms, fragment size %d %dms\n"),
Debug(3, "audio/oss: buffer size %d %dms, fragment size %d %dms\n",
bi.fragsize * bi.fragstotal, (bi.fragsize * bi.fragstotal * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample), bi.fragsize,
OssFragmentTime);
@@ -1889,7 +1920,7 @@ static int OssSetup(int *freq, int *channels, int use_ac3)
OssStartThreshold = RingBufferFreeBytes(OssRingBuffer);
}
Info(_("audio/oss: delay %u ms\n"), (OssStartThreshold * 1000)
Info(_("audio/oss: delay %ums\n"), (OssStartThreshold * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
return ret;
@@ -1914,7 +1945,7 @@ void OssPause(void)
*/
static void OssInit(void)
{
OssRingBuffer = RingBufferNew(48000 * 8 * 2); // ~1s 8ch 16bit
OssRingBuffer = RingBufferNew(2 * 48000 * 8 * 2); // ~2s 8ch 16bit
OssInitPCM();
OssInitMixer();
@@ -2002,9 +2033,9 @@ static int NoopUsedBytes(void)
**
** @returns audio delay in time stamps.
*/
static uint64_t NoopGetDelay(void)
static int64_t NoopGetDelay(void)
{
return 0UL;
return 0L;
}
/**
@@ -2079,8 +2110,9 @@ static void *AudioPlayHandlerThread(void *dummy)
// cond_wait can return, without signal!
} while (!AudioRunning);
Debug(3, "audio: ----> %d ms\n", (AudioUsedBytes() * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
Debug(3, "audio: ----> %dms start\n", (AudioUsedBytes() * 1000)
/ (!AudioSampleRate + !AudioChannels +
AudioSampleRate * AudioChannels * AudioBytesProSample));
pthread_mutex_unlock(&AudioMutex);
@@ -2115,7 +2147,6 @@ static void *AudioPlayHandlerThread(void *dummy)
}
#endif
Debug(3, "audio: play start\n");
AudioUsedModule->Thread();
}
@@ -2182,11 +2213,14 @@ static const AudioModule *AudioModules[] = {
*/
void AudioEnqueue(const void *samples, int count)
{
if (!AudioSampleRate || !AudioChannels) {
return; // not setup
}
if (0) {
static uint32_t last;
static uint32_t tick;
static uint32_t max = 101;
uint64_t delay;
int64_t delay;
delay = AudioGetDelay();
tick = GetMsTicks();
@@ -2202,7 +2236,7 @@ void AudioEnqueue(const void *samples, int count)
// Update audio clock (stupid gcc developers thinks INT64_C is unsigned)
if (AudioPTS != (int64_t) INT64_C(0x8000000000000000)) {
AudioPTS +=
((int64_t) count * 90000) / (AudioSampleRate * AudioChannels *
((int64_t) count * 90 * 1000) / (AudioSampleRate * AudioChannels *
AudioBytesProSample);
}
}
@@ -2253,7 +2287,7 @@ int AudioUsedBytes(void)
**
** @returns audio delay in time stamps.
*/
uint64_t AudioGetDelay(void)
int64_t AudioGetDelay(void)
{
return AudioUsedModule->GetDelay();
}
@@ -2267,9 +2301,8 @@ void AudioSetClock(int64_t pts)
{
#ifdef DEBUG
if (AudioPTS != pts) {
Debug(4, "audio: set clock to %#012" PRIx64 " %#012" PRIx64 " pts\n",
AudioPTS, pts);
Debug(4, "audio: set clock %s -> %s pts\n", Timestamp2String(AudioPTS),
Timestamp2String(pts));
}
#endif
AudioPTS = pts;

View File

@@ -32,7 +32,7 @@ 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 uint64_t AudioGetDelay(void); ///< get current audio delay
extern int64_t AudioGetDelay(void); ///< get current audio delay
extern void AudioSetClock(int64_t); ///< set audio clock base
extern int64_t AudioGetClock(); ///< get current audio clock
extern void AudioSetVolume(int); ///< set volume

35
codec.c
View File

@@ -558,6 +558,11 @@ void CodecVideoDecode(VideoDecoder * decoder, const AVPacket * avpkt)
video_ctx->frame_number, used);
}
if (used != pkt->size) {
// ffmpeg 0.8.7 dislikes our seq_end_h264 and enters endless loop here
if (used == 0 && pkt->size == 5 && pkt->data[4] == 0x0A) {
Warning("codec: ffmpeg 0.8.x workaround used\n");
return;
}
if (used >= 0 && used < pkt->size) {
// some tv channels, produce this
Debug(4,
@@ -723,8 +728,8 @@ void CodecAudioOpen(AudioDecoder * audio_decoder, const char *name,
if (audio_codec->capabilities & CODEC_CAP_TRUNCATED) {
Debug(3, "codec: audio can use truncated packets\n");
// we do not send complete frames
audio_decoder->AudioCtx->flags |= CODEC_FLAG_TRUNCATED;
// we send only complete frames
// audio_decoder->AudioCtx->flags |= CODEC_FLAG_TRUNCATED;
}
audio_decoder->SampleRate = 0;
audio_decoder->Channels = 0;
@@ -795,6 +800,10 @@ void CodecSetAudioDownmix(int onoff)
** ffmpeg L R C Ls Rs -> alsa L R Ls Rs C
** ffmpeg L R C LFE Ls Rs -> alsa L R Ls Rs C LFE
** ffmpeg L R C LFE Ls Rs Rl Rr -> alsa L R Ls Rs C LFE Rl Rr
**
** @param buf[IN,OUT] sample buffer
** @param size size of sample buffer in bytes
** @param channels number of channels interleaved in sample buffer
*/
static void CodecReorderAudioFrame(int16_t * buf, int size, int channels)
{
@@ -931,7 +940,12 @@ static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts)
if (audio_decoder->AvResample && audio_decoder->DriftCorr) {
int distance;
distance = (pts_diff * audio_decoder->HwSampleRate) / (90 * 1000);
// try workaround for buggy ffmpeg 0.10
if (abs(audio_decoder->DriftCorr) < 2000) {
distance = (pts_diff * audio_decoder->HwSampleRate) / (900 * 1000);
} else {
distance = (pts_diff * audio_decoder->HwSampleRate) / (90 * 1000);
}
av_resample_compensate(audio_decoder->AvResample,
audio_decoder->DriftCorr / 10, distance);
}
@@ -950,10 +964,6 @@ static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder)
int err;
int isAC3;
audio_ctx = audio_decoder->AudioCtx;
audio_decoder->PassthroughAC3 = CodecPassthroughAC3;
// FIXME: use swr_convert from swresample (only in ffmpeg!)
if (audio_decoder->ReSample) {
audio_resample_close(audio_decoder->ReSample);
@@ -965,9 +975,16 @@ static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder)
audio_decoder->RemainCount = 0;
}
audio_ctx = audio_decoder->AudioCtx;
Debug(3, "codec/audio: format change %dHz %d channels %s\n",
audio_ctx->sample_rate, audio_ctx->channels,
CodecPassthroughAC3 ? "pass-through" : "");
audio_decoder->SampleRate = audio_ctx->sample_rate;
audio_decoder->HwSampleRate = audio_ctx->sample_rate;
audio_decoder->Channels = audio_ctx->channels;
audio_decoder->PassthroughAC3 = CodecPassthroughAC3;
// SPDIF/HDMI passthrough
if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
audio_decoder->HwChannels = 2;
@@ -1034,8 +1051,7 @@ static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder)
**
** @param audio_decoder audio decoder data
** @param data samples data
** @param count number of samples
**
** @param count number of bytes in sample data
*/
void CodecAudioEnqueue(AudioDecoder * audio_decoder, int16_t * data, int count)
{
@@ -1152,7 +1168,6 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
// update audio clock
if (avpkt->pts != (int64_t) AV_NOPTS_VALUE) {
CodecAudioSetClock(audio_decoder, avpkt->pts);
}
// FIXME: must first play remainings bytes, than change and play new.
if (audio_decoder->PassthroughAC3 != CodecPassthroughAC3

25
misc.h
View File

@@ -107,6 +107,31 @@ static inline void Syslog(const int level, const char *format, ...)
#define Debug(level, fmt...) /* disabled */
#endif
#ifndef AV_NOPTS_VALUE
#define AV_NOPTS_VALUE INT64_C(0x8000000000000000)
#endif
/**
** Nice time-stamp string.
**
** @param ts dvb time stamp
*/
static inline const char *Timestamp2String(int64_t ts)
{
static char buf[4][16];
static int idx;
if (ts == (int64_t) AV_NOPTS_VALUE) {
return "--:--:--.---";
}
idx = (idx + 1) % 3;
snprintf(buf[idx], sizeof(buf[idx]), "%2d:%02d:%02d.%03d",
(int)(ts / (90 * 3600000)), (int)((ts / (90 * 60000)) % 60),
(int)((ts / (90 * 1000)) % 60), (int)((ts / 90) % 1000));
return buf[idx];
}
/**
** Get ticks in ms.
**

View File

@@ -47,6 +47,10 @@
#include "video.h"
#include "codec.h"
#ifdef DEBUG
static int H264Dump(const uint8_t * data, int size);
#endif
//////////////////////////////////////////////////////////////////////////////
// Variables
//////////////////////////////////////////////////////////////////////////////
@@ -311,6 +315,12 @@ static inline int FastAc3Check(const uint8_t * p)
if (p[1] != 0x77) {
return 0;
}
if ((p[4] & 0xC0) == 0xC0) { // invalid sample rate
return 0;
}
if ((p[4] & 0x3F) > 37) { // invalid frame size
return 0;
}
return 1;
}
@@ -334,7 +344,7 @@ static int Ac3Check(const uint8_t * data, int size)
// crc1 crc1 fscod|frmsizcod
fscod = data[4] >> 6;
frmsizcod = data[4] & 0x3F;
frmsizcod = data[4] & 0x3F; // invalid is checked by fast
frame_size = Ac3FrameSizeTable[frmsizcod][fscod] * 2;
if (frame_size + 2 > size) {
@@ -421,12 +431,25 @@ typedef struct _pes_demux_
int64_t DTS; ///< decode time stamp
} PesDemux;
///
/// Reset packetized elementary stream demuxer.
///
static void PesReset(PesDemux * pesdx)
{
pesdx->State = PES_INIT;
pesdx->Index = 0;
pesdx->Skip = 0;
pesdx->StartCode = -1;
pesdx->PTS = AV_NOPTS_VALUE;
pesdx->DTS = AV_NOPTS_VALUE;
}
///
/// Initialize a packetized elementary stream demuxer.
///
/// @param pesdx packetized elementary stream demuxer
///
void PesInit(PesDemux * pesdx)
static void PesInit(PesDemux * pesdx)
{
memset(pesdx, 0, sizeof(*pesdx));
pesdx->Size = PES_MAX_PAYLOAD;
@@ -434,20 +457,7 @@ void PesInit(PesDemux * pesdx)
if (!pesdx->Buffer) {
Fatal(_("pesdemux: out of memory\n"));
}
pesdx->PTS = AV_NOPTS_VALUE; // reset
pesdx->DTS = AV_NOPTS_VALUE;
}
///
/// Reset packetized elementary stream demuxer.
///
void PesReset(PesDemux * pesdx)
{
pesdx->State = PES_INIT;
pesdx->Index = 0;
pesdx->Skip = 0;
pesdx->PTS = AV_NOPTS_VALUE;
pesdx->DTS = AV_NOPTS_VALUE;
PesReset(pesdx);
}
///
@@ -458,7 +468,8 @@ void PesReset(PesDemux * pesdx)
/// @param size number of payload data bytes
/// @param is_start flag, start of pes packet
///
void PesParse(PesDemux * pesdx, const uint8_t * data, int size, int is_start)
static void PesParse(PesDemux * pesdx, const uint8_t * data, int size,
int is_start)
{
const uint8_t *p;
const uint8_t *q;
@@ -605,6 +616,8 @@ void PesParse(PesDemux * pesdx, const uint8_t * data, int size, int is_start)
Debug(3, "pesdemux: pes start code id %#02x\n", code);
// FIXME: need to save start code id?
pesdx->StartCode = code;
// we could have already detect a valid stream type
// don't switch to codec 'none'
}
pesdx->State = PES_HEADER;
@@ -664,11 +677,11 @@ void PesParse(PesDemux * pesdx, const uint8_t * data, int size, int is_start)
// only private stream 1, has sub streams
pesdx->State = PES_START;
}
//pesdx->HeaderIndex = 0;
//pesdx->Index = 0;
}
break;
#if 0
// Played with PlayAudio
case PES_LPCM_HEADER: // lpcm header
n = pesdx->HeaderSize - pesdx->HeaderIndex;
if (n > size) {
@@ -745,6 +758,7 @@ void PesParse(PesDemux * pesdx, const uint8_t * data, int size, int is_start)
AudioEnqueue(pesdx->Buffer, pesdx->Index);
pesdx->Index = 0;
break;
#endif
}
} while (size > 0);
}
@@ -782,7 +796,7 @@ static PesDemux PesDemuxAudio[1]; ///< audio demuxer
///
/// @returns number of bytes consumed from buffer.
///
int TsDemuxer(TsDemux * tsdx, const uint8_t * data, int size)
static int TsDemuxer(TsDemux * tsdx, const uint8_t * data, int size)
{
const uint8_t *p;
@@ -864,25 +878,28 @@ int PlayAudio(const uint8_t * data, int size, uint8_t id)
// channel switch: SetAudioChannelDevice: SetDigitalAudioDevice:
if (StreamFreezed) { // stream freezed
return 0;
}
if (SkipAudio || !MyAudioDecoder) { // skip audio
return size;
}
if (StreamFreezed) { // stream freezed
return 0;
}
if (NewAudioStream) {
// FIXME: does this clear the audio ringbuffer?
// this clears the audio ringbuffer indirect, open and setup does it
CodecAudioClose(MyAudioDecoder);
AudioSetBufferTime(0);
AudioCodecID = CODEC_ID_NONE;
AudioChannelID = -1;
NewAudioStream = 0;
}
// Don't overrun audio buffers on replay
// hard limit buffer full: don't overrun audio buffers on replay
if (AudioFreeBytes() < AUDIO_MIN_BUFFER_FREE) {
return 0;
}
// soft limit buffer full
if (AudioUsedBytes() > AUDIO_MIN_BUFFER_FREE && VideoGetBuffers() > 3) {
return 0;
}
// PES header 0x00 0x00 0x01 ID
// ID 0xBD 0xC0-0xCF
@@ -924,7 +941,7 @@ int PlayAudio(const uint8_t * data, int size, uint8_t id)
AudioAvPkt->stream_index = 0;
}
if (AudioChannelID != id) {
if (AudioChannelID != id) { // id changed audio track changed
AudioChannelID = id;
AudioCodecID = CODEC_ID_NONE;
}
@@ -1063,6 +1080,8 @@ int PlayAudio(const uint8_t * data, int size, uint8_t id)
/**
** Play transport stream audio packet.
**
** VDR can have buffered data belonging to previous channel!
**
** @param data data of exactly one complete TS packet
** @param size size of TS packet (always TS_PACKET_SIZE)
**
@@ -1072,26 +1091,31 @@ int PlayTsAudio(const uint8_t * data, int size)
{
static TsDemux tsdx[1];
if (StreamFreezed) { // stream freezed
return 0;
}
if (SkipAudio || !MyAudioDecoder) { // skip audio
return size;
}
// Don't overrun audio buffers on replay
if (AudioFreeBytes() < AUDIO_MIN_BUFFER_FREE) {
if (StreamFreezed) { // stream freezed
return 0;
}
if (NewAudioStream) {
// FIXME: does this clear the audio ringbuffer?
// this clears the audio ringbuffer indirect, open and setup does it
CodecAudioClose(MyAudioDecoder);
// max time between audio packets 200ms + 24ms hw buffer
AudioSetBufferTime(264);
AudioCodecID = CODEC_ID_NONE;
AudioChannelID = -1;
NewAudioStream = 0;
PesReset(PesDemuxAudio);
}
// hard limit buffer full: don't overrun audio buffers on replay
if (AudioFreeBytes() < AUDIO_MIN_BUFFER_FREE) {
return 0;
}
// soft limit buffer full
if (AudioUsedBytes() > AUDIO_MIN_BUFFER_FREE && VideoGetBuffers() > 3) {
return 0;
}
return TsDemuxer(tsdx, data, size);
}
@@ -1113,13 +1137,16 @@ void SetVolumeDevice(int volume)
#include <alsa/iatomic.h> // portable atomic_t
#ifdef DEBUG
uint32_t VideoSwitch; ///< debug video switch ticks
#endif
static volatile char NewVideoStream; ///< flag new video stream
static volatile char ClosingVideoStream; ///< flag closing video stream
static VideoHwDecoder *MyHwDecoder; ///< video hw decoder
static VideoDecoder *MyVideoDecoder; ///< video decoder
static enum CodecID VideoCodecID; ///< current codec id
static const char *X11DisplayName; ///< x11 display name
const char *X11DisplayName; ///< x11 display name
static volatile char Usr1Signal; ///< true got usr1 signal
#define VIDEO_BUFFER_SIZE (512 * 1024) ///< video PES buffer default size
@@ -1131,9 +1158,9 @@ static int VideoPacketRead; ///< read pointer
static atomic_t VideoPacketsFilled; ///< how many of the buffer is used
static volatile char VideoClearBuffers; ///< clear video buffers
static volatile char VideoClearClose; ///< clear video buffers upto close
static volatile char SkipVideo; ///< skip video
static volatile char VideoTrickSpeed; ///< current trick speed
static volatile char VideoTrickCounter; ///< current trick speed counter
static volatile char CurrentTrickSpeed; ///< current trick speed
#ifdef DEBUG
static int VideoMaxPacketSize; ///< biggest used packet buffer
@@ -1264,6 +1291,7 @@ static void VideoNextPacket(int codec_id)
memset(avpkt->data + avpkt->stream_index, 0, FF_INPUT_BUFFER_PADDING_SIZE);
avpkt->priv = (void *)(size_t) codec_id;
//H264Dump(avpkt->data, avpkt->stream_index);
// advance packet write
VideoPacketWrite = (VideoPacketWrite + 1) % VIDEO_PACKET_MAX;
@@ -1321,6 +1349,10 @@ void FixPacketForFFMpeg(VideoDecoder * MyVideoDecoder, AVPacket * avpkt)
/**
** Decode from PES packet ringbuffer.
**
** @retval 0 packet decoded
** @retval 1 stream paused
** @retval -1 empty stream
*/
int VideoDecode(void)
{
@@ -1341,34 +1373,30 @@ int VideoDecode(void)
VideoClearBuffers = 0;
return 1;
}
if (VideoTrickSpeed) {
if (VideoTrickCounter++ < VideoTrickSpeed * 2) {
usleep(5 * 1000);
return 1;
}
VideoTrickCounter = 0;
}
filled = atomic_read(&VideoPacketsFilled);
if (!filled) {
return -1;
}
#if 0
int f;
// clearing for normal channel switch has no advantage
if (VideoClearClose /*|| ClosingVideoStream */ ) {
int f;
// FIXME: flush buffers, if close is in the queue
for (f = 0; f < filled; ++f) {
avpkt = &VideoPacketRb[(VideoPacketRead + f) % VIDEO_PACKET_MAX];
if ((int)(size_t) avpkt->priv == CODEC_ID_NONE) {
printf("video: close\n");
if (f) {
atomic_sub(f, &VideoPacketsFilled);
VideoPacketRead = (VideoPacketRead + f) % VIDEO_PACKET_MAX;
// flush buffers, if close is in the queue
for (f = 0; f < filled; ++f) {
avpkt = &VideoPacketRb[(VideoPacketRead + f) % VIDEO_PACKET_MAX];
if ((int)(size_t) avpkt->priv == CODEC_ID_NONE) {
if (f) {
Debug(3, "video: cleared upto close\n");
atomic_sub(f, &VideoPacketsFilled);
VideoPacketRead = (VideoPacketRead + f) % VIDEO_PACKET_MAX;
VideoClearClose = 0;
}
break;
}
break;
}
ClosingVideoStream = 0;
}
#endif
avpkt = &VideoPacketRb[VideoPacketRead];
//
@@ -1376,11 +1404,13 @@ int VideoDecode(void)
//
switch ((int)(size_t) avpkt->priv) {
case CODEC_ID_NONE:
ClosingVideoStream = 0;
if (last_codec_id != CODEC_ID_NONE) {
last_codec_id = CODEC_ID_NONE;
CodecVideoClose(MyVideoDecoder);
goto skip;
}
// FIXME: look if more close are in the queue
// size can be zero
goto skip;
case CODEC_ID_MPEG2VIDEO:
@@ -1430,6 +1460,11 @@ int VideoDecode(void)
}
}
if (ClosingVideoStream) { // closing don't sync
avpkt->pts = AV_NOPTS_VALUE;
avpkt->dts = AV_NOPTS_VALUE;
}
if (last_codec_id == CODEC_ID_MPEG2VIDEO) {
FixPacketForFFMpeg(MyVideoDecoder, avpkt);
} else {
@@ -1500,6 +1535,30 @@ static void StopVideo(void)
#ifdef DEBUG
/**
** Dump h264 video packet.
**
** Function to Dump a h264 packet, not needed.
*/
static int H264Dump(const uint8_t * data, int size)
{
printf("H264:");
do {
if (size < 4) {
printf("\n");
return -1;
}
if (!data[0] && !data[1] && data[2] == 0x01) {
printf("%02x ", data[3]);
}
++data;
--size;
} while (size);
printf("\n");
return 0;
}
/**
** Validate mpeg video packet.
**
@@ -1560,10 +1619,6 @@ int PlayVideo(const uint8_t * data, int size)
int z;
int l;
if (Usr1Signal) { // x11 server ready
Usr1Signal = 0;
StartVideo();
}
if (!MyVideoDecoder) { // no x11 video started
return size;
}
@@ -1574,7 +1629,7 @@ int PlayVideo(const uint8_t * data, int size)
return 0;
}
if (NewVideoStream) { // channel switched
Debug(3, "video: new stream %d\n", GetMsTicks() - VideoSwitch);
Debug(3, "video: new stream %dms\n", GetMsTicks() - VideoSwitch);
// FIXME: hack to test results
if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 1) {
Debug(3, "video: new video stream lost\n");
@@ -1583,6 +1638,9 @@ int PlayVideo(const uint8_t * data, int size)
}
VideoNextPacket(CODEC_ID_NONE);
VideoCodecID = CODEC_ID_NONE;
// clear clock until new stream starts
VideoSetClock(MyHwDecoder, AV_NOPTS_VALUE);
ClosingVideoStream = 1;
NewVideoStream = 0;
}
// must be a PES start code
@@ -1590,72 +1648,68 @@ int PlayVideo(const uint8_t * data, int size)
Error(_("[softhddev] invalid PES video packet\n"));
return size;
}
n = data[8]; // header size
// 0xBE, filler, padding stream
if (data[3] == PES_PADDING_STREAM) { // from DVD plugin
return size;
}
if (size < 9 + n + 4) { // wrong size
n = data[8]; // header size
if (size <= 9 + n) { // wrong size
if (size == 9 + n) {
Warning(_("[softhddev] empty video packet\n"));
} else {
Error(_("[softhddev] invalid video packet %d bytes\n"), size);
Error(_("[softhddev] invalid video packet %d/%d bytes\n"), 9 + n,
size);
}
return size;
}
// buffer full: needed for replay
if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 1) {
// hard limit buffer full: needed for replay
if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 3) {
return 0;
}
// soft limit buffer full
if (atomic_read(&VideoPacketsFilled) > 3
&& AudioUsedBytes() > AUDIO_MIN_BUFFER_FREE) {
return 0;
}
// get pts/dts
pts = AV_NOPTS_VALUE;
if (data[7] & 0x80) {
pts =
(int64_t) (data[9] & 0x0E) << 29 | data[10] << 22 | (data[11] &
0xFE) << 14 | data[12] << 7 | (data[13] & 0xFE) >> 1;
#ifdef DEBUG
if (!(data[13] & 1) || !(data[11] & 1) || !(data[9] & 1)) {
Error(_("[softhddev] invalid pts in video packet\n"));
return size;
}
//Debug(3, "video: pts %#012" PRIx64 "\n", pts);
if (data[13] != (((pts & 0x7F) << 1) | 1)) {
abort();
}
if (data[12] != ((pts >> 7) & 0xFF)) {
abort();
}
if (data[11] != ((((pts >> 15) & 0x7F) << 1) | 1)) {
abort();
}
if (data[10] != ((pts >> 22) & 0xFF)) {
abort();
}
if ((data[9] & 0x0F) != (((pts >> 30) << 1) | 1)) {
abort();
}
#endif
}
check = data + 9 + n;
if (0) {
printf("%02x: %02x %02x %02x %02x %02x %02x %02x\n", data[6], check[0],
check[1], check[2], check[3], check[4], check[5], check[6]);
}
#if 1 // FIXME: test code for better h264 detection
z = 0;
l = size - 9 - n;
z = 0;
while (!*check) { // count leading zeros
if (--l < 4) {
if (l < 3) {
Warning(_("[softhddev] empty video packet %d bytes\n"), size);
return size;
z = 0;
break;
}
--l;
++check;
++z;
}
// H264 Access Unit Delimiter 0x00 0x00 0x00 0x01 0x09
// H264 NAL AUD Access Unit Delimiter 0x00 0x00 0x00 0x01 0x09
if ((data[6] & 0xC0) == 0x80 && z > 2 && check[0] == 0x01
&& check[1] == 0x09) {
if (VideoCodecID == CODEC_ID_H264) {
if (CurrentTrickSpeed && pts != (int64_t) AV_NOPTS_VALUE) {
// H264 NAL End of Sequence
static uint8_t seq_end_h264[] =
{ 0x00, 0x00, 0x00, 0x01, 0x0A };
// NAL SPS sequence parameter set
if ((check[7] & 0x1F) == 0x07) {
VideoNextPacket(CODEC_ID_H264);
VideoEnqueue(AV_NOPTS_VALUE, seq_end_h264,
sizeof(seq_end_h264));
}
}
VideoNextPacket(CODEC_ID_H264);
} else {
Debug(3, "video: h264 detected\n");
@@ -1683,7 +1737,6 @@ int PlayVideo(const uint8_t * data, int size)
return size;
}
// this happens when vdr sends incomplete packets
if (VideoCodecID == CODEC_ID_NONE) {
Debug(3, "video: not detected\n");
return size;
@@ -1701,66 +1754,6 @@ int PlayVideo(const uint8_t * data, int size)
}
return size;
#else
// FIXME: no valid mpeg2/h264 detection yet
// FIXME: better skip all zero's >3 && 0x01 0x09 h264, >2 && 0x01 -> mpeg2
// PES_VIDEO_STREAM 0xE0 or PES start code
//(data[6] & 0xC0) != 0x80 ||
if ((!check[0] && !check[1] && check[2] == 0x1)) {
if (VideoCodecID == CODEC_ID_MPEG2VIDEO) {
VideoNextPacket(CODEC_ID_MPEG2VIDEO);
} else {
Debug(3, "video: mpeg2 detected ID %02x\n", check[3]);
VideoCodecID = CODEC_ID_MPEG2VIDEO;
}
#ifdef DEBUG
if (ValidateMpeg(data, size)) {
Debug(3, "softhddev/video: invalid mpeg2 video packet\n");
}
#endif
// Access Unit Delimiter
} else if ((data[6] & 0xC0) == 0x80 && !check[0] && !check[1]
&& !check[2] && check[3] == 0x1 && check[4] == 0x09) {
if (VideoCodecID == CODEC_ID_H264) {
VideoNextPacket(CODEC_ID_H264);
} else {
Debug(3, "video: h264 detected\n");
VideoCodecID = CODEC_ID_H264;
}
// Access Unit Delimiter (BBC-HD)
// FIXME: the 4 offset are try & error selected
} else if ((data[6] & 0xC0) == 0x80 && !check[4 + 0] && !check[4 + 1]
&& !check[4 + 2] && check[4 + 3] == 0x1 && check[4 + 4] == 0x09) {
if (VideoCodecID == CODEC_ID_H264) {
VideoNextPacket(CODEC_ID_H264);
} else {
Debug(3, "video: h264 detected\n");
VideoCodecID = CODEC_ID_H264;
}
} else {
// this happens when vdr sends incomplete packets
if (VideoCodecID == CODEC_ID_NONE) {
Debug(3, "video: not detected\n");
return size;
}
// incomplete packets produce artefacts after channel switch
// packet < 65526 is the last split packet, detect it here for
// better latency
if (size < 65526 && VideoCodecID == CODEC_ID_MPEG2VIDEO) {
// mpeg codec supports incomplete packets
// waiting for a full complete packages, increases needed delays
VideoEnqueue(pts, check, size - 9 - n);
VideoNextPacket(CODEC_ID_MPEG2VIDEO);
return size;
}
}
// SKIP PES header
VideoEnqueue(pts, check, size - 9 - n);
return size;
#endif
}
/// call VDR support function
@@ -1847,9 +1840,6 @@ uint8_t *GrabImage(int *size, int jpeg, int quality, int width, int height)
}
return NULL;
}
if (width != -1 && height != -1) {
Warning(_("softhddev: scaling unsupported\n"));
}
return VideoGrab(size, &width, &height, 1);
}
@@ -1866,7 +1856,9 @@ int SetPlayMode(int play_mode)
if (MyVideoDecoder) { // tell video parser we have new stream
if (VideoCodecID != CODEC_ID_NONE) {
NewVideoStream = 1;
#ifdef DEBUG
VideoSwitch = GetMsTicks();
#endif
}
}
if (MyAudioDecoder) { // tell audio parser we have new stream
@@ -1874,14 +1866,39 @@ int SetPlayMode(int play_mode)
NewAudioStream = 1;
}
}
if (play_mode == 2 || play_mode == 3) {
Debug(3, "softhddev: FIXME: audio only, silence video errors\n");
switch (play_mode) {
case 1: // audio/video from player
break;
case 2: // audio only
Debug(3, "softhddev: FIXME: audio only, silence video errors\n");
VideoSetClock(MyHwDecoder, AV_NOPTS_VALUE);
break;
case 3: // audio only, black screen
Debug(3, "softhddev: FIXME: audio only, silence video errors\n");
VideoSetClock(MyHwDecoder, AV_NOPTS_VALUE);
break;
case 4: // video only
break;
}
Play();
return 1;
}
/**
** Gets the current System Time Counter, which can be used to
** synchronize audio, video and subtitles.
*/
int64_t GetSTC(void)
{
if (MyHwDecoder) {
return VideoGetClock(MyHwDecoder);
}
Error(_("softhddev: %s called without hw decoder\n"), __FUNCTION__);
return AV_NOPTS_VALUE;
}
/**
** Set trick play speed.
**
@@ -1892,8 +1909,13 @@ int SetPlayMode(int play_mode)
*/
void TrickSpeed(int speed)
{
VideoTrickSpeed = speed;
VideoTrickCounter = 0;
CurrentTrickSpeed = speed;
if (MyHwDecoder) {
VideoSetTrickSpeed(MyHwDecoder, speed);
} else {
// can happen, during startup
Debug(3, "softhddev: %s called without hw decoder\n", __FUNCTION__);
}
StreamFreezed = 0;
}
@@ -1910,9 +1932,11 @@ void Clear(void)
//NewAudioStream = 1;
// FIXME: audio avcodec_flush_buffers, video is done by VideoClearBuffers
// wait for empty buffers
for (i = 0; VideoClearBuffers && i < 20; ++i) {
usleep(1 * 1000);
}
Debug(3, "[softhddev]%s: buffers %d\n", __FUNCTION__, VideoGetBuffers());
}
/**
@@ -1920,9 +1944,7 @@ void Clear(void)
*/
void Play(void)
{
VideoTrickSpeed = 0;
VideoTrickCounter = 0;
StreamFreezed = 0;
TrickSpeed(0); // normal play
SkipAudio = 0;
AudioPlay();
}
@@ -1956,7 +1978,8 @@ void StillPicture(const uint8_t * data, int size)
{
int i;
static uint8_t seq_end_mpeg[] = { 0x00, 0x00, 0x01, 0xB7 };
static uint8_t seq_end_h264[] = { 0x00, 0x00, 0x00, 0x01, 0x10 };
// H264 NAL End of Sequence
static uint8_t seq_end_h264[] = { 0x00, 0x00, 0x00, 0x01, 0x0A };
// must be a PES start code
if (size < 9 || !data || data[0] || data[1] || data[2] != 0x01) {
@@ -1968,14 +1991,13 @@ void StillPicture(const uint8_t * data, int size)
// FIXME: should detect codec, see PlayVideo
Error(_("[softhddev] no codec known for still picture\n"));
}
//Clear(); // flush video buffers
// +1 future for deinterlace
for (i = -1; i < (VideoCodecID == CODEC_ID_MPEG2VIDEO ? 3 : 17); ++i) {
//if ( 1 ) {
// FIXME: can check video backend, if a frame was produced.
// output for max reference frames
for (i = 0; i < (VideoCodecID == CODEC_ID_MPEG2VIDEO ? 3 : 17); ++i) {
const uint8_t *split;
int n;
// FIXME: vdr pes recordings sends mixed audio/video
if ((data[3] & 0xF0) == 0xE0) { // PES packet
split = data;
n = size;
@@ -1983,15 +2005,31 @@ void StillPicture(const uint8_t * data, int size)
do {
int len;
len = (split[4] << 8) + split[5];
if (!len || len + 6 > n) {
PlayVideo(split, n); // feed remaining bytes
#ifdef DEBUG
if (split[0] || split[1] || split[2] != 0x01) {
Error(_("[softhddev] invalid still video packet\n"));
break;
}
PlayVideo(split, len + 6); // feed it
#endif
len = (split[4] << 8) + split[5];
if (!len || len + 6 > n) {
// video only
if ((data[3] & 0xF0) == 0xE0) {
while (!PlayVideo(split, n)) { // feed remaining bytes
}
}
break;
}
if ((data[3] & 0xF0) == 0xE0) {
// video only
while (!PlayVideo(split, len + 6)) { // feed it
}
}
split += 6 + len;
n -= 6 + len;
} while (n > 6);
VideoNextPacket(VideoCodecID); // terminate last packet
if (VideoCodecID == CODEC_ID_H264) {
@@ -2013,25 +2051,61 @@ void StillPicture(const uint8_t * data, int size)
VideoNextPacket(VideoCodecID); // terminate last packet
}
}
// wait for empty buffers
for (i = 0; VideoGetBuffers() && i < 30; ++i) {
usleep(10 * 1000);
}
Debug(3, "[softhddev]%s: buffers %d\n", __FUNCTION__, VideoGetBuffers());
}
/**
** Poll if device is ready. Called by replay.
**
** This function is useless, the return value is ignored and
** all buffers are overrun by vdr.
**
** The dvd plugin is using this correct.
**
** @param timeout timeout to become ready in ms
**
** @retval true if ready
** @retval false if busy
*/
int Poll(int timeout)
{
// buffers are too full
if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX * 2 / 3
|| AudioFreeBytes() < AUDIO_MIN_BUFFER_FREE * 2) {
if (timeout) { // let display thread work
usleep(timeout * 1000);
// poll is only called during replay, flush buffers after replay
VideoClearClose = 1;
for (;;) {
#if 0
int empty;
int t;
// buffers are too full
empty = atomic_read(&VideoPacketsFilled) < VIDEO_PACKET_MAX * 1 / 4
|| AudioUsedBytes() < AUDIO_MIN_BUFFER_FREE * 2;
if (empty || !timeout) {
return empty;
}
return atomic_read(&VideoPacketsFilled) < VIDEO_PACKET_MAX * 2 / 3
&& AudioFreeBytes() > AUDIO_MIN_BUFFER_FREE;
#else
int full;
int t;
// one buffer is full
full = AudioFreeBytes() >= AUDIO_MIN_BUFFER_FREE
|| atomic_read(&VideoPacketsFilled) < VIDEO_PACKET_MAX - 3;
if (!full || !timeout) {
return !full;
}
#endif
t = 15;
if (timeout < t) {
t = timeout;
}
usleep(t * 1000); // let display thread work
timeout -= t;
}
return 1;
}
/**
@@ -2056,6 +2130,10 @@ int Flush(int timeout)
/**
** Get OSD size and aspect.
**
** @param width[OUT] width of OSD
** @param height[OUT] height of OSD
** @param aspect[OUT] aspect ratio (4/3, 16/9, ...) of OSD
*/
void GetOsdSize(int *width, int *height, double *aspect)
{
@@ -2087,9 +2165,17 @@ void OsdClose(void)
/**
** Draw an OSD pixmap.
**
** @param x x-coordinate on screen of argb image
** @param y y-coordinate on screen of argb image
** @paran height height in pixel of argb image
** @paran width width in pixel of argb image
** @param argb height * width 32bit ARGB image data
*/
void OsdDrawARGB(int x, int y, int height, int width, const uint8_t * argb)
{
// wakeup display for showing remote learning dialog
VideoDisplayWakeup();
VideoOsdDrawARGB(x, y, height, width, argb);
}
@@ -2106,6 +2192,7 @@ const char *CommandLineHelp(void)
" -d display\tdisplay of x11 server (fe. :0.0)\n"
" -f\t\tstart with fullscreen window (only with window manager)\n"
" -g geometry\tx11 window geometry wxh+x+y\n"
" -v device\tvideo device (va-api, vdpau, noop)\n"
" -s\t\tstart in suspended mode\n" " -x\t\tstart x11 server\n"
" -w workaround\tenable/disable workarounds\n"
"\tno-hw-decoder\t\tdisable hw decoder, use software decoder only\n"
@@ -2126,7 +2213,7 @@ int ProcessArgs(int argc, char *const argv[])
// Parse arguments.
//
for (;;) {
switch (getopt(argc, argv, "-a:c:d:fg:p:sw:x")) {
switch (getopt(argc, argv, "-a:c:d:fg:p:sv:w:x")) {
case 'a': // audio device for pcm
AudioSetDevice(optarg);
continue;
@@ -2150,6 +2237,13 @@ int ProcessArgs(int argc, char *const argv[])
return 0;
}
continue;
case 'v': // video driver
VideoSetDevice(optarg);
#ifdef USE_VDPAU
// FIXME: this is a big hack
ConfigVdpauDecoder = !strcasecmp(optarg, "vdpau");
#endif
continue;
case 'x': // x11 server
ConfigStartX11Server = 1;
continue;
@@ -2301,7 +2395,6 @@ void SoftHdDeviceExit(void)
StopVideo();
CodecExit();
//VideoPacketExit();
if (ConfigStartX11Server) {
Debug(3, "x-setup: Stop x11 server\n");
@@ -2389,11 +2482,23 @@ void Stop(void)
#endif
}
/**
** Perform any cleanup or other regular tasks.
*/
void Housekeeping(void)
{
}
/**
** Main thread hook, periodic called from main thread.
*/
void MainThreadHook(void)
{
if (Usr1Signal) { // x11 server ready
Usr1Signal = 0;
StartVideo();
VideoDisplayWakeup();
}
}
//////////////////////////////////////////////////////////////////////////////
@@ -2419,30 +2524,26 @@ void Suspend(int video, int audio, int dox11)
SkipVideo = 1;
SkipAudio = 1;
pthread_mutex_unlock(&SuspendLockMutex);
if (audio || video) {
pthread_mutex_lock(&SuspendLockMutex);
if (audio) {
AudioExit();
if (MyAudioDecoder) {
CodecAudioClose(MyAudioDecoder);
CodecAudioDelDecoder(MyAudioDecoder);
MyAudioDecoder = NULL;
}
NewAudioStream = 0;
av_free_packet(AudioAvPkt);
if (audio) {
AudioExit();
if (MyAudioDecoder) {
CodecAudioClose(MyAudioDecoder);
CodecAudioDelDecoder(MyAudioDecoder);
MyAudioDecoder = NULL;
}
if (video) {
StopVideo();
}
pthread_mutex_unlock(&SuspendLockMutex);
NewAudioStream = 0;
av_free_packet(AudioAvPkt);
}
if (video) {
StopVideo();
}
if (dox11) {
// FIXME: stop x11, if started
}
pthread_mutex_unlock(&SuspendLockMutex);
}
/**

View File

@@ -51,6 +51,8 @@ extern "C"
/// C plugin set play mode
extern int SetPlayMode(int);
/// C plugin get current system time counter
extern int64_t GetSTC(void);
/// C plugin set trick speed
extern void TrickSpeed(int);
/// C plugin clears all video and audio data from the device
@@ -79,6 +81,8 @@ extern "C"
extern int Start(void);
/// C plugin stop code
extern void Stop(void);
/// C plugin house keeping
extern void Housekeeping(void);
/// C plugin main thread hook
extern void MainThreadHook(void);

View File

@@ -36,6 +36,8 @@
extern "C"
{
#include "video.h"
extern const char *X11DisplayName; ///< x11 display name
extern void AudioPoller(void);
extern void CodecSetAudioPassthrough(int);
extern void CodecSetAudioDownmix(int);
@@ -43,15 +45,23 @@ extern "C"
//////////////////////////////////////////////////////////////////////////////
static const char *const VERSION = "0.4.9"
/// vdr-plugin version number.
/// Makefile extracts the version number for generating the file name
/// for the distribution archive.
static const char *const VERSION = "0.5.0"
#ifdef GIT_REV
"-GIT" GIT_REV
#endif
;
/// vdr-plugin description.
static const char *const DESCRIPTION =
trNOOP("A software and GPU emulated HD device");
/// vdr-plugin text of main menu entry
static const char *MAINMENUENTRY = trNOOP("SoftHdDevice");
/// single instance of softhddevice plugin device.
static class cSoftHdDevice *MyDevice;
//////////////////////////////////////////////////////////////////////////////
@@ -67,9 +77,9 @@ static char ConfigMakePrimary; ///< config primary wanted
static char ConfigHideMainMenuEntry; ///< config hide main menu entry
static uint32_t ConfigVideoBackground; ///< config video background color
static int ConfigVideoSkipLines; ///< config skip lines top/bottom
static int ConfigVideoStudioLevels; ///< config use studio levels
static int ConfigVideo60HzMode; ///< config use 60Hz display mode
static char ConfigVideoStudioLevels; ///< config use studio levels
static char ConfigVideo60HzMode; ///< config use 60Hz display mode
static char ConfigVideoSoftStartSync; ///< config use softstart sync
/// config deinterlace
static int ConfigVideoDeinterlace[RESOLUTIONS];
@@ -89,6 +99,12 @@ static int ConfigVideoSharpen[RESOLUTIONS];
/// config scaling
static int ConfigVideoScaling[RESOLUTIONS];
/// config cut top and bottom pixels
static int ConfigVideoCutTopBottom[RESOLUTIONS];
/// config cut left and right pixels
static int ConfigVideoCutLeftRight[RESOLUTIONS];
static int ConfigVideoAudioDelay; ///< config audio delay
static int ConfigAudioPassthrough; ///< config audio pass-through
static int ConfigAudioDownmix; ///< config audio downmix
@@ -100,11 +116,12 @@ static int ConfigAutoCropTolerance; ///< auto crop detection tolerance
static char ConfigSuspendClose; ///< suspend should close devices
static char ConfigSuspendX11; ///< suspend should stop x11
static volatile char DoMakePrimary; ///< flag switch primary
static volatile int DoMakePrimary; ///< switch primary device to this
#define SUSPEND_EXTERNAL -1 ///< play external suspend mode
#define SUSPEND_NORMAL 0 ///< normal suspend mode
#define SUSPEND_DETACHED 1 ///< detached suspend mode
#define NOT_SUSPENDED 0 ///< not suspend mode
#define SUSPEND_NORMAL 1 ///< normal suspend mode
#define SUSPEND_DETACHED 2 ///< detached suspend mode
static char SuspendMode; ///< suspend mode
//////////////////////////////////////////////////////////////////////////////
@@ -113,18 +130,42 @@ static char SuspendMode; ///< suspend mode
// C Callbacks
//////////////////////////////////////////////////////////////////////////////
/**
** Soft device plugin remote class.
*/
class cSoftRemote:public cRemote
{
public:
/**
** Soft device remote class constructor.
**
** @param name remote name
*/
cSoftRemote(const char *name):cRemote(name)
{
}
/**
** Put keycode into vdr event queue.
**
** @param code key code
** @param repeat flag key repeated
** @param release flag key released
*/
bool Put(const char *code, bool repeat = false, bool release = false) {
return cRemote::Put(code, repeat, release);
}
};
/**
** Feed key press as remote input (called from C part).
**
** @param keymap target keymap "XKeymap" name
** @param key pressed/released key name
** @param repeat repeated key flag
** @param release released key flag
*/
extern "C" void FeedKeyPress(const char *keymap, const char *key, int repeat,
int release)
{
@@ -140,7 +181,7 @@ extern "C" void FeedKeyPress(const char *keymap, const char *key, int repeat,
break;
}
}
// if remote not already exists, create it
if (remote) {
csoft = (cSoftRemote *) remote;
} else {
@@ -166,16 +207,16 @@ extern "C" void FeedKeyPress(const char *keymap, const char *key, int repeat,
*/
class cSoftOsd:public cOsd
{
//int Level; ///< level: subtitle
public:
cSoftOsd(int, int, uint);
virtual ~ cSoftOsd(void);
virtual void Flush(void);
virtual void SetActive(bool);
static volatile char Dirty; ///< flag force redraw everything
cSoftOsd(int, int, uint); ///< constructor
virtual ~ cSoftOsd(void); ///< destructor
virtual void Flush(void); ///< commits all data to the hardware
virtual void SetActive(bool); ///< sets OSD to be the active one
};
static volatile char OsdDirty; ///< flag force redraw everything
volatile char cSoftOsd::Dirty; ///< flag force redraw everything
/**
** Sets this OSD to be the active one.
@@ -194,12 +235,21 @@ void cSoftOsd::SetActive(bool on)
}
cOsd::SetActive(on);
if (on) {
OsdDirty = 1;
Dirty = 1;
} else {
OsdClose();
}
}
/**
** Constructor OSD.
**
** Initializes the OSD with the given coordinates.
**
** @param left x-coordinate of osd on display
** @param top y-coordinate of osd on display
** @param level level of the osd (smallest is shown)
*/
cSoftOsd::cSoftOsd(int left, int top, uint level)
:cOsd(left, top, level)
{
@@ -208,10 +258,14 @@ cSoftOsd::cSoftOsd(int left, int top, uint level)
OsdHeight(), left, top, level);
*/
//this->Level = level;
SetActive(true);
}
/**
** OSD Destructor.
**
** Shuts down the OSD.
*/
cSoftOsd::~cSoftOsd(void)
{
//dsyslog("[softhddev]%s:\n", __FUNCTION__);
@@ -277,7 +331,7 @@ void cSoftOsd::Flush(void)
int y2;
// get dirty bounding box
if (OsdDirty) { // forced complete update
if (Dirty) { // forced complete update
x1 = 0;
y1 = 0;
x2 = bitmap->Width() - 1;
@@ -323,7 +377,7 @@ void cSoftOsd::Flush(void)
// FIXME: reuse argb
free(argb);
}
OsdDirty = 0;
cSoftOsd::Dirty = 0;
return;
}
@@ -419,14 +473,17 @@ class cMenuSetupSoft:public cMenuSetupPage
int HideMainMenuEntry;
uint32_t Background;
uint32_t BackgroundAlpha;
int SkipLines;
int StudioLevels;
int _60HzMode;
int SoftStartSync;
int Scaling[RESOLUTIONS];
int Deinterlace[RESOLUTIONS];
int SkipChromaDeinterlace[RESOLUTIONS];
int InverseTelecine[RESOLUTIONS];
int Denoise[RESOLUTIONS];
int Sharpen[RESOLUTIONS];
int CutTopBottom[RESOLUTIONS];
int CutLeftRight[RESOLUTIONS];
int AudioDelay;
int AudioPassthrough;
int AudioDownmix;
@@ -497,12 +554,15 @@ cMenuSetupSoft::cMenuSetupSoft(void)
(int *)&Background, 0, 0x00FFFFFF));
Add(new cMenuEditIntItem(tr("video background color (Alpha)"),
(int *)&BackgroundAlpha, 0, 0xFF));
SkipLines = ConfigVideoSkipLines;
Add(new cMenuEditIntItem(tr("Skip lines top+bot (pixel)"), &SkipLines, 0,
64));
StudioLevels = ConfigVideoStudioLevels;
Add(new cMenuEditBoolItem(tr("Use studio levels (vdpau only)"),
&StudioLevels, trVDR("no"), trVDR("yes")));
_60HzMode = ConfigVideo60HzMode;
Add(new cMenuEditBoolItem(tr("60hz display mode"), &_60HzMode, trVDR("no"),
trVDR("yes")));
SoftStartSync = ConfigVideoSoftStartSync;
Add(new cMenuEditBoolItem(tr("soft start a/v sync"), &SoftStartSync,
trVDR("no"), trVDR("yes")));
for (i = 0; i < RESOLUTIONS; ++i) {
Add(SeparatorItem(resolution[i]));
@@ -523,6 +583,13 @@ cMenuSetupSoft::cMenuSetupSoft(void)
Sharpen[i] = ConfigVideoSharpen[i];
Add(new cMenuEditIntItem(tr("Sharpen (-1000..1000) (vdpau)"),
&Sharpen[i], -1000, 1000, tr("blur max"), tr("sharpen max")));
CutTopBottom[i] = ConfigVideoCutTopBottom[i];
Add(new cMenuEditIntItem(tr("Cut top and bottom (pixel)"),
&CutTopBottom[i], 0, 250));
CutLeftRight[i] = ConfigVideoCutLeftRight[i];
Add(new cMenuEditIntItem(tr("Cut left and right (pixel)"),
&CutLeftRight[i], 0, 250));
}
//
// audio
@@ -576,10 +643,12 @@ void cMenuSetupSoft::Store(void)
ConfigVideoBackground = Background << 8 | (BackgroundAlpha & 0xFF);
SetupStore("Background", ConfigVideoBackground);
VideoSetBackground(ConfigVideoBackground);
SetupStore("SkipLines", ConfigVideoSkipLines = SkipLines);
VideoSetSkipLines(ConfigVideoSkipLines);
SetupStore("StudioLevels", ConfigVideoStudioLevels = StudioLevels);
VideoSetStudioLevels(ConfigVideoStudioLevels);
SetupStore("60HzMode", ConfigVideo60HzMode = _60HzMode);
VideoSet60HzMode(ConfigVideo60HzMode);
SetupStore("SoftStartSync", ConfigVideoSoftStartSync = SoftStartSync);
VideoSetSoftStartSync(ConfigVideoSoftStartSync);
for (i = 0; i < RESOLUTIONS; ++i) {
char buf[128];
@@ -598,6 +667,11 @@ void cMenuSetupSoft::Store(void)
SetupStore(buf, ConfigVideoDenoise[i] = Denoise[i]);
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "Sharpen");
SetupStore(buf, ConfigVideoSharpen[i] = Sharpen[i]);
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "CutTopBottom");
SetupStore(buf, ConfigVideoCutTopBottom[i] = CutTopBottom[i]);
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "CutLeftRight");
SetupStore(buf, ConfigVideoCutLeftRight[i] = CutLeftRight[i]);
}
VideoSetScaling(ConfigVideoScaling);
VideoSetDeinterlace(ConfigVideoDeinterlace);
@@ -605,6 +679,8 @@ void cMenuSetupSoft::Store(void)
VideoSetInverseTelecine(ConfigVideoInverseTelecine);
VideoSetDenoise(ConfigVideoDenoise);
VideoSetSharpen(ConfigVideoSharpen);
VideoSetCutTopBottom(ConfigVideoCutTopBottom);
VideoSetCutLeftRight(ConfigVideoCutLeftRight);
SetupStore("AudioDelay", ConfigVideoAudioDelay = AudioDelay);
VideoSetAudioDelay(ConfigVideoAudioDelay);
@@ -653,23 +729,23 @@ cSoftHdPlayer::~cSoftHdPlayer()
//////////////////////////////////////////////////////////////////////////////
/**
** Dummy control for suspend mode.
** Dummy control class for suspend mode.
*/
class cSoftHdControl:public cControl
{
public:
static cSoftHdPlayer *Player; ///< dummy player
virtual void Hide(void)
virtual void Hide(void) ///< hide control
{
}
virtual eOSState ProcessKey(eKeys);
virtual eOSState ProcessKey(eKeys); ///< process input events
cSoftHdControl(void);
cSoftHdControl(void); ///< control constructor
virtual ~ cSoftHdControl();
virtual ~ cSoftHdControl(); ///< control destructor
};
cSoftHdPlayer *cSoftHdControl::Player;
cSoftHdPlayer *cSoftHdControl::Player; ///< dummy player instance
/**
** Handle a key event.
@@ -686,7 +762,7 @@ eOSState cSoftHdControl::ProcessKey(eKeys key)
Player = NULL;
}
Resume();
SuspendMode = 0;
SuspendMode = NOT_SUSPENDED;
return osEnd;
}
return osContinue;
@@ -711,8 +787,7 @@ cSoftHdControl::~cSoftHdControl()
Player = NULL;
}
dsyslog("[softhddev]%s: resume\n", __FUNCTION__);
//Resume();
dsyslog("[softhddev]%s: dummy player stopped\n", __FUNCTION__);
}
//////////////////////////////////////////////////////////////////////////////
@@ -769,6 +844,24 @@ static void HandleHotkey(int code)
case 12: // toggle pass-through
CodecSetAudioPassthrough(ConfigAudioPassthrough ^= 1);
break;
case 13: // decrease audio delay
ConfigVideoAudioDelay -= 10;
VideoSetAudioDelay(ConfigVideoAudioDelay);
break;
case 14: // increase audio delay
ConfigVideoAudioDelay += 10;
VideoSetAudioDelay(ConfigVideoAudioDelay);
break;
case 20: // disable full screen
VideoSetFullscreen(0);
break;
case 21: // enable full screen
VideoSetFullscreen(1);
break;
case 22: // toggle full screen
VideoSetFullscreen(-1);
break;
default:
esyslog(tr("[softhddev]: hot key %d is not supported\n"), code);
break;
@@ -827,7 +920,8 @@ eOSState cSoftHdMenu::ProcessKey(eKeys key)
switch (state) {
case osUser1:
if (!cSoftHdControl::Player) { // not already suspended
// not already suspended
if (SuspendMode == NOT_SUSPENDED && !cSoftHdControl::Player) {
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(ConfigSuspendClose, ConfigSuspendClose,
@@ -928,11 +1022,12 @@ void cSoftHdDevice::MakePrimaryDevice(bool on)
cDevice::MakePrimaryDevice(on);
if (on) {
new cSoftOsdProvider();
if (SuspendMode == SUSPEND_DETACHED) {
Resume();
SuspendMode = 0;
SuspendMode = NOT_SUSPENDED;
}
} else if (!SuspendMode) {
} else if (SuspendMode == NOT_SUSPENDED) {
Suspend(1, 1, 0);
SuspendMode = SUSPEND_DETACHED;
}
@@ -952,6 +1047,9 @@ cSpuDecoder *cSoftHdDevice::GetSpuDecoder(void)
#endif
/**
** Tells whether this device has a MPEG decoder.
*/
bool cSoftHdDevice::HasDecoder(void) const
{
return true;
@@ -994,12 +1092,12 @@ bool cSoftHdDevice::SetPlayMode(ePlayMode play_mode)
break;
}
if (SuspendMode) {
if (SuspendMode != NOT_SUSPENDED) {
if (SuspendMode != SUSPEND_EXTERNAL) {
return true;
return false;
}
Resume();
SuspendMode = 0;
SuspendMode = NOT_SUSPENDED;
}
return::SetPlayMode(play_mode);
@@ -1013,7 +1111,7 @@ int64_t cSoftHdDevice::GetSTC(void)
{
//dsyslog("[softhddev]%s:\n", __FUNCTION__);
return::VideoGetClock();
return::GetSTC();
}
/**
@@ -1077,6 +1175,9 @@ void cSoftHdDevice::Mute(void)
/**
** Display the given I-frame as a still picture.
**
** @param data pes or ts data of a frame
** @param length length of data area
*/
void cSoftHdDevice::StillPicture(const uchar * data, int length)
{
@@ -1096,6 +1197,9 @@ void cSoftHdDevice::StillPicture(const uchar * data, int length)
**
** @param poller file handles (unused)
** @param timeout_ms timeout in ms to become ready
**
** @retval true if ready
** @retval false if busy
*/
bool cSoftHdDevice::Poll(
__attribute__ ((unused)) cPoller & poller, int timeout_ms)
@@ -1124,7 +1228,7 @@ bool cSoftHdDevice::Flush(int timeout_ms)
** device has an MPEG decoder).
*/
void cSoftHdDevice:: SetVideoDisplayFormat(eVideoDisplayFormat
video_display_format)
video_display_format)
{
static int last = -1;
@@ -1137,7 +1241,7 @@ void cSoftHdDevice:: SetVideoDisplayFormat(eVideoDisplayFormat
last = video_display_format;
::VideoSetDisplayFormat(video_display_format);
OsdDirty = 1;
cSoftOsd::Dirty = 1;
}
}
@@ -1324,7 +1428,7 @@ class cPluginSoftHdDevice:public cPlugin
virtual bool Initialize(void);
virtual bool Start(void);
virtual void Stop(void);
// virtual void Housekeeping(void);
virtual void Housekeeping(void);
virtual void MainThreadHook(void);
virtual const char *MainMenuEntry(void);
virtual cOsdObject *MainMenuAction(void);
@@ -1407,7 +1511,7 @@ bool cPluginSoftHdDevice::Start(void)
// Must be done in the main thread
dsyslog("[softhddev] makeing softhddevice %d the primary device!",
MyDevice->DeviceNumber());
DoMakePrimary = 1;
DoMakePrimary = MyDevice->DeviceNumber() + 1;
} else {
isyslog("[softhddev] softhddevice %d is not the primary device!",
MyDevice->DeviceNumber());
@@ -1423,6 +1527,10 @@ bool cPluginSoftHdDevice::Start(void)
return true;
}
/**
** Shutdown plugin. Stop any background activities the plugin is
** performing.
*/
void cPluginSoftHdDevice::Stop(void)
{
//dsyslog("[softhddev]%s:\n", __FUNCTION__);
@@ -1430,8 +1538,6 @@ void cPluginSoftHdDevice::Stop(void)
::Stop();
}
#if 0
/**
** Perform any cleanup or other regular tasks.
*/
@@ -1439,10 +1545,18 @@ void cPluginSoftHdDevice::Housekeeping(void)
{
dsyslog("[softhddev]%s:\n", __FUNCTION__);
// ::Housekeeping();
}
// check if user is inactive, automatic enter suspend mode
// FIXME: cControl prevents shutdown, disable this until fixed
if (0 && SuspendMode == NOT_SUSPENDED && ShutdownHandler.IsUserInactive()) {
// don't overwrite already suspended suspend mode
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
SuspendMode = SUSPEND_NORMAL;
}
#endif
::Housekeeping();
}
/**
** Create main menu entry.
@@ -1461,21 +1575,6 @@ cOsdObject *cPluginSoftHdDevice::MainMenuAction(void)
{
//dsyslog("[softhddev]%s:\n", __FUNCTION__);
#if 0
//MyDevice->StopReplay();
if (!cSoftHdControl::Player) { // not already suspended
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
SuspendMode = SUSPEND_NORMAL;
if (ShutdownHandler.GetUserInactiveTime()) {
dsyslog("[softhddev]%s: set user inactive\n", __FUNCTION__);
ShutdownHandler.SetUserInactive();
}
}
return NULL;
#endif
return new cSoftHdMenu("SoftHdDevice");
}
@@ -1487,17 +1586,12 @@ void cPluginSoftHdDevice::MainThreadHook(void)
{
//dsyslog("[softhddev]%s:\n", __FUNCTION__);
if (DoMakePrimary && MyDevice) {
dsyslog("[softhddev]%s: switching primary device\n", __FUNCTION__);
cDevice::SetPrimaryDevice(MyDevice->DeviceNumber() + 1);
if (DoMakePrimary) {
dsyslog("[softhddev]%s: switching primary device to %d\n",
__FUNCTION__, DoMakePrimary);
cDevice::SetPrimaryDevice(DoMakePrimary);
DoMakePrimary = 0;
}
// check if user is inactive, automatic enter suspend mode
if (ShutdownHandler.IsUserInactive()) {
// this is regular called, but guarded against double calls
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
SuspendMode = SUSPEND_NORMAL;
}
::MainThreadHook();
}
@@ -1538,10 +1632,6 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
VideoSetBackground(ConfigVideoBackground = strtoul(value, NULL, 0));
return true;
}
if (!strcasecmp(name, "SkipLines")) {
VideoSetSkipLines(ConfigVideoSkipLines = atoi(value));
return true;
}
if (!strcasecmp(name, "StudioLevels")) {
VideoSetStudioLevels(ConfigVideoStudioLevels = atoi(value));
return true;
@@ -1550,6 +1640,10 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
VideoSet60HzMode(ConfigVideo60HzMode = atoi(value));
return true;
}
if (!strcasecmp(name, "SoftStartSync")) {
VideoSetSoftStartSync(ConfigVideoSoftStartSync = atoi(value));
return true;
}
for (i = 0; i < RESOLUTIONS; ++i) {
char buf[128];
@@ -1590,6 +1684,19 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
VideoSetSharpen(ConfigVideoSharpen);
return true;
}
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "CutTopBottom");
if (!strcasecmp(name, buf)) {
ConfigVideoCutTopBottom[i] = atoi(value);
VideoSetCutTopBottom(ConfigVideoCutTopBottom);
return true;
}
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "CutLeftRight");
if (!strcasecmp(name, buf)) {
ConfigVideoCutLeftRight[i] = atoi(value);
VideoSetCutLeftRight(ConfigVideoCutLeftRight);
return true;
}
}
if (!strcasecmp(name, "AudioDelay")) {
@@ -1647,6 +1754,48 @@ bool cPluginSoftHdDevice::Service(const char *Id, void *Data)
// cPlugin SVDRP
//----------------------------------------------------------------------------
/**
** SVDRP commands help text.
** FIXME: translation?
*/
static const char *SVDRPHelpText[] = {
"SUSP\n" " Suspend plugin.\n\n"
" The plugin is suspended to save energie. Depending on the setup\n"
" 'softhddevice.Suspend.Close = 0' only the video and audio output\n"
" is stopped or with 'softhddevice.Suspend.Close = 1' the video\n"
" and audio devices are closed.\n"
" If 'softhddevice.Suspend.X11 = 1' is set and the X11 server was\n"
" started by the plugin, the X11 server would also be closed.\n"
" (Stopping X11 while suspended isn't supported yet)\n",
"RESU\n" " Resume plugin.\n\n"
" Resume the suspended plugin. The plugin could be suspended by\n"
" the command line option '-s' or by a previous SUSP command.\n"
" If the x11 server was stopped by the plugin, it will be\n"
" restarted.",
"DETA\n" " Detach plugin.\n\n"
" The plugin will be detached from the audio, video and DVB\n"
" devices. Other programs or plugins can use them now.\n",
"ATTA <-d display>\n" " Attach plugin.\n\n"
" Attach the plugin to audio, video and DVB devices.\n"
" Use -d display (f.e. -d :0.0) to use another X11 display.\n",
"PRIM <n>\n" " Make <n> the primary device.\n\n"
" <n> is the number of device. Without number softhddevice becomes\n"
" the primary device. If becoming primary, the plugin is attached\n"
" to the devices. If loosing primary, the plugin is detached from\n"
" the devices.",
"HOTK key\n" " Execute hotkey.\n\n"
" key is the hotkey number, following are supported:\n"
" 10: disable audio pass-through\n"
" 11: enable audio pass-through\n"
" 12: toggle audio pass-through\n"
" 13: decrease audio delay by 10ms\n"
" 14: increase audio delay by 10ms\n"
" 20: disable fullscreen\n"
" 21: enable fullscreen\n"
" 22: toggle fullscreen\n",
NULL
};
/**
** Return SVDRP commands help pages.
**
@@ -1655,17 +1804,7 @@ bool cPluginSoftHdDevice::Service(const char *Id, void *Data)
*/
const char **cPluginSoftHdDevice::SVDRPHelpPages(void)
{
// FIXME: translation?
static const char *text[] = {
"SUSP\n" " Suspend plugin.\n",
"RESU\n" " Resume plugin.\n",
"DETA\n" " Detach plugin.\n",
"ATTA\n" " Attach plugin.\n",
"HOTK key\n" " Execute hotkey.\n",
NULL
};
return text;
return SVDRPHelpText;
}
/**
@@ -1676,21 +1815,25 @@ const char **cPluginSoftHdDevice::SVDRPHelpPages(void)
** @param reply_code reply code
*/
cString cPluginSoftHdDevice::SVDRPCommand(const char *command,
__attribute__ ((unused)) const char *option,
__attribute__ ((unused)) int &reply_code)
const char *option, __attribute__ ((unused)) int &reply_code)
{
if (!strcasecmp(command, "SUSP")) {
if (cSoftHdControl::Player) { // already suspended
return "SoftHdDevice already suspended";
}
// should be after suspend, but SetPlayMode resumes
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
SuspendMode = SUSPEND_NORMAL;
if (SuspendMode != NOT_SUSPENDED) {
return "SoftHdDevice already detached";
}
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
SuspendMode = SUSPEND_NORMAL;
return "SoftHdDevice is suspended";
}
if (!strcasecmp(command, "RESU")) {
if (SuspendMode == NOT_SUSPENDED) {
return "SoftHdDevice already resumed";
}
if (SuspendMode != SUSPEND_NORMAL) {
return "can't resume SoftHdDevice";
}
@@ -1701,26 +1844,33 @@ cString cPluginSoftHdDevice::SVDRPCommand(const char *command,
cControl::Shutdown(); // not need, if not suspended
}
Resume();
SuspendMode = 0;
SuspendMode = NOT_SUSPENDED;
return "SoftHdDevice is resumed";
}
if (!strcasecmp(command, "DETA")) {
if (SuspendMode == SUSPEND_DETACHED) {
return "SoftHdDevice already detached";
}
if (cSoftHdControl::Player) { // already suspended
if (SuspendMode == SUSPEND_DETACHED) {
return "SoftHdDevice already detached";
}
return "can't suspend SoftHdDevice already suspended";
}
Suspend(1, 1, 0);
SuspendMode = SUSPEND_DETACHED;
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(1, 1, 0);
SuspendMode = SUSPEND_DETACHED;
return "SoftHdDevice is detached";
}
if (!strcasecmp(command, "ATTA")) {
if (SuspendMode != SUSPEND_DETACHED) {
return "can't attach SoftHdDevice not detached";
}
if (!strncmp(option, "-d ", 3)) {
// FIXME: loose memory here
X11DisplayName = strdup(option + 3);
} else if (!strncmp(option, "-d", 2)) {
// FIXME: loose memory here
X11DisplayName = strdup(option + 2);
}
if (ShutdownHandler.GetUserInactiveTime()) {
ShutdownHandler.SetUserInactiveTimeout();
}
@@ -1728,7 +1878,7 @@ cString cPluginSoftHdDevice::SVDRPCommand(const char *command,
cControl::Shutdown(); // not need, if not suspended
}
Resume();
SuspendMode = 0;
SuspendMode = NOT_SUSPENDED;
return "SoftHdDevice is attached";
}
if (!strcasecmp(command, "HOTK")) {
@@ -1738,6 +1888,17 @@ cString cPluginSoftHdDevice::SVDRPCommand(const char *command,
HandleHotkey(hotk);
return "hot-key executed";
}
if (!strcasecmp(command, "PRIM")) {
int primary;
primary = strtol(option, NULL, 0);
if (!primary && MyDevice) {
primary = MyDevice->DeviceNumber() + 1;
}
dsyslog("[softhddev] switching primary device to %d\n", primary);
DoMakePrimary = primary;
return "switching primary device requested";
}
return NULL;
}

1803
video.c

File diff suppressed because it is too large Load Diff

23
video.h
View File

@@ -35,6 +35,7 @@ typedef struct _video_hw_decoder_ VideoHwDecoder;
//----------------------------------------------------------------------------
extern char VideoIgnoreRepeatPict; ///< disable repeat pict warning
extern int VideoAudioDelay; ///< audio/video delay
//----------------------------------------------------------------------------
// Prototypes
@@ -77,12 +78,18 @@ extern void VideoPollEvent(void);
/// Wakeup display handler.
extern void VideoDisplayWakeup(void);
/// Set video device.
extern void VideoSetDevice(const char *);
/// Set video geometry.
extern int VideoSetGeometry(const char *);
/// Set 60Hz display mode.
extern void VideoSet60HzMode(int);
/// Set soft start audio/video sync.
extern void VideoSetSoftStartSync(int);
/// Set video output position.
extern void VideoSetOutputPosition(int, int, int, int);
@@ -113,8 +120,11 @@ extern void VideoSetDenoise(int[]);
/// Set sharpen.
extern void VideoSetSharpen(int[]);
/// Set skip lines.
extern void VideoSetSkipLines(int);
/// Set cut top and bottom.
extern void VideoSetCutTopBottom(int[]);
/// Set cut left and right.
extern void VideoSetCutLeftRight(int[]);
/// Set studio levels.
extern void VideoSetStudioLevels(int);
@@ -137,7 +147,14 @@ extern void VideoOsdDrawARGB(int, int, int, int, const uint8_t *);
/// Get OSD size.
extern void VideoGetOsdSize(int *, int *);
extern int64_t VideoGetClock(void); ///< Get video clock.
/// Set video clock.
extern void VideoSetClock(VideoHwDecoder *, int64_t);
/// Get video clock.
extern int64_t VideoGetClock(const VideoHwDecoder *);
/// Set trick play speed.
extern void VideoSetTrickSpeed(VideoHwDecoder *, int);
/// Grab screen.
extern uint8_t *VideoGrab(int *, int *, int *, int);