33 Commits
0.3.5 ... 0.4.5

Author SHA1 Message Date
Johns
eed708b9ea Release version 0.4.5. 2012-01-28 13:44:50 +01:00
Johns
60a7c36fa6 Add configurable skip lines at video top + bottom. 2012-01-28 01:44:50 +01:00
Johns
4d74ed1bfc Add auto-crop tolerance configuration. 2012-01-27 23:49:05 +01:00
Johns
c3b924a239 Reduces audio latency, increases audio buffer time. 2012-01-27 23:33:10 +01:00
Johns
f8d198636b Video bug fix.
Made video_test working again.
Disabled VA-API Intel vaAssociateSubpicture workaround.
Fix bug: Must release lock for VideoPollEvent.
Allow faster video and audio sync.
Fix bug: Software decoder use vaPutImage with Intel backend.
Fix bug: Artefacts are shown after mpeg2 channel switch.
Fix bug: VideoReleaseSurface called after VideoExit.
2012-01-27 21:08:37 +01:00
Johns
bcf6ecabc1 Support external players. 2012-01-26 15:00:49 +01:00
Johns
9063b4e3ff Add VDPAU display preemption support. 2012-01-25 15:31:49 +01:00
Johns
e3681812bd Remove pass-through test code. 2012-01-25 15:31:18 +01:00
Johns
9d14522121 Add jpeg support to ebuild. 2012-01-25 15:30:22 +01:00
2dff69dc14 Add support for grab jpeg image. 2012-01-24 22:40:06 +01:00
Johns
5668fa22d2 Video cleanup.
More functions uses new module interface.
Fix bug: VaapiOsdExit doesn't deassociate osd surface.
Fix bug: First OSD can show random pixels.
2012-01-24 22:25:33 +01:00
Johns
c7cebe1aeb Wait for X11 exit and kill it, if not. 2012-01-24 22:20:17 +01:00
Johns
037f582bad Fix still picture handling. 2012-01-24 16:37:11 +01:00
Johns
6ca4d3c44f Fix dead-lock in VdpauExit. 2012-01-24 10:02:39 +01:00
Johns
2ac2eb39c6 Workaround for dead-lock in VdpauExit. 2012-01-24 00:32:07 +01:00
Johns
217545542d Add ac3 pass-through device to OSS module. 2012-01-23 20:23:05 +01:00
Johns
5f43803236 VDPAU: Add primitive software scaler to grab image 2012-01-23 20:04:15 +01:00
Johns
993d831190 VA-API: Add auto-crop support. 2012-01-23 15:40:59 +01:00
Johns
1969b2a0a7 Fix bug: close codec missing. 2012-01-22 22:46:52 +01:00
Johns
0fad02285d AC3 device should be called 'ALSA_AC3_DEVICE'. 2012-01-22 20:53:27 +01:00
Johns
9546233175 Suspend can close and open video and audio device. 2012-01-22 20:49:43 +01:00
Johns
98d2e0f728 Cleanups and Codec..Del.. prototypes. 2012-01-22 17:07:08 +01:00
970493fb23 Use different alsa device for AC3/pass-through. 2012-01-22 16:54:22 +01:00
Johns
329dbc5f07 Add dummy player and control for suspend mode. 2012-01-22 11:12:57 +01:00
Johns
bc8a13e1ef Call VdpauMixerSetup only, if mixer setup. 2012-01-21 21:56:19 +01:00
Johns
bd7e6143c7 Buffertime compile time configurable in ms. 2012-01-21 21:46:47 +01:00
Johns
fa27a1c73a Release Version 0.4.0. 2012-01-21 15:56:45 +01:00
Johns
e32857a27a VDPAU: Add screenshot support. 2012-01-20 21:46:22 +01:00
Johns
5ba88bb822 Use common module prefix. 2012-01-20 19:56:06 +01:00
Johns
0422b6aa5a VDPAU: Add auto-crop support. 2012-01-20 15:33:37 +01:00
Johns
eb024558de VDPAU: Changed OSD alpha calculation. 2012-01-19 22:58:02 +01:00
Johns
1593d5dd83 Fix bug: Used VideoSharpen for denoise settings.
Instant update deinterlace/... configuration changes.
2012-01-19 21:28:38 +01:00
Johns
09f62307d4 Fix bug: AudioExit called without AudioInit crash. 2012-01-19 17:01:02 +01:00
15 changed files with 2318 additions and 696 deletions

View File

@@ -1,4 +1,59 @@
User johns
Date:
Release Version 0.4.5
Add configurable skip lines at video top and bottom.
Add auto-crop tolerance configuration.
Reduces audio latency, increases audio buffer time.
Made video_test working again.
Disabled VA-API Intel vaAssociateSubpicture workaround.
Fix bug: Must release lock for VideoPollEvent.
Allow faster video and audio sync.
Fix bug: Software decoder use vaPutImage with intel backend.
Fix bug: Artefacts are shown after mpeg2 channel switch.
Fix bug: VideoReleaseSurface called after VideoExit.
Support external players.
Add VDPAU display preemption support.
User m.Rcu
Date: Tue Jan 24 22:38:30 CET 2012
Add support for grab jpeg image.
User johns
Date: Tue Jan 24 22:25:33 CET 2012
Fix bug: VaapiOsdExit doesn't deassociate osd surface.
Fix bug: First OSD can show random pixels.
Wait for X11 exit and kill it, if not.
Fix still picture handling.
Fix for dead-lock in VdpauExit.
Workaround for dead-lock in VdpauExit.
VDPAU: Add very primitive software scaler for grab image.
VA-API: Add auto-crop support.
Suspend can close/open X11 window, connection and audio device.
User Morone
Date: Sun Jan 22 16:43:23 CET 2012
Use different alsa devices for AC3/pass-through and pcm.
User johns
Date: Sun Jan 22 11:12:57 CET 2012
Add dummy player and control for suspend mode.
Buffertime compile time configurable in ms.
Date: Sat Jan 21 15:49:16 CET 2012
Release Version 0.4.0
VDPAU: Add grab image support.
VDPAU: Add auto-crop support.
VDPAU: Changed OSD alpha calculation.
Fix bug: Used VideoSharpen for denoise settings.
Instant update deinterlace/... configuration changes.
Fix bug: AudioExit called without AudioInit crash.
Date: Thu Jan 19 15:58:40 CET 2012
Release Version 0.3.5

View File

@@ -23,6 +23,7 @@ CONFIG := #-DDEBUG
CONFIG += $(shell pkg-config --exists vdpau && echo "-DUSE_VDPAU")
CONFIG += $(shell pkg-config --exists libva && echo "-DUSE_VAAPI")
CONFIG += $(shell pkg-config --exists alsa && echo "-DUSE_ALSA")
CONFIG += $(shell ls /usr/lib/libjpeg* >/dev/null 2>&1 && echo "-DUSE_JPEG")
CONFIG += -DUSE_OSS
### The C++ compiler and options:
@@ -89,7 +90,9 @@ LIBS += -lrt \
$(if $(findstring USE_VAAPI,$(CONFIG)), \
`pkg-config --libs libva-x11 libva-glx libva`) \
$(if $(findstring USE_ALSA,$(CONFIG)), \
`pkg-config --libs alsa`)
`pkg-config --libs alsa`) \
$(if $(findstring USE_JPEG,$(CONFIG)), \
-ljpeg)
### The object files (add further files here):
@@ -171,6 +174,6 @@ indent:
indent $$i; unexpand -a $$i > $$i.up; mv $$i.up $$i; \
done
video_test: video.c
video_test: video.c Makefile
$(CC) -DVIDEO_TEST -DVERSION='"$(VERSION)"' $(CFLAGS) $(LDFLAGS) $< $(LIBS) \
-o $@

View File

@@ -24,17 +24,20 @@ A software and GPU emulated HD output device plugin for VDR.
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 planned: Video VA-API/Opengl
o planned: Video VDPAU/Opengl
o planned: Video CPU/Xv
o planned: Video CPU/Opengl
o planned: Software Deinterlacer
o planned: Improved Software Deinterlacer (yadif or/and ffmpeg filters)
o planned: Video XvBA/XvBA
o Audio FFMpeg/Alsa/Analog
o Audio FFMpeg/Alsa/Digital
o Audio FFMpeg/OSS/Analog
o Alsa HDMI/SPDIF Passthrough
o planned: OSS HDMI/SPDIF Passthrough
o planned: atmo light support
To compile you must have the 'requires' installed.
@@ -76,6 +79,8 @@ Setup: environment
only if alsa is configured
ALSA_DEVICE=default
alsa PCM device name
ALSA_AC3_DEVICE=
alsa AC3/pass-though device name
ALSA_MIXER=default
alsa control device name
ALSA_MIXER_CHANNEL=PCM
@@ -83,6 +88,8 @@ Setup: environment
only if oss is configured
OSS_AUDIODEV=/dev/dsp
oss dsp device name
OSS_AC3_AUDIODEV=
oss AC3/pass-though device name
OSS_MIXERDEV=/dev/mixer
oss mixer device name
OSS_MIXER_CHANNEL=pcm
@@ -125,6 +132,28 @@ Setup: /etc/vdr/setup.conf
softhddevice.AudioPassthrough = 0
0 = none, 1 = AC-3
for AC-3 the pass-through device is used.
softhddevice.AutoCrop.Interval = 0
0 disables auto-crop
n each 'n' frames auto-crop is checked.
softhddevice.AutoCrop.Delay = 0
if auto-crop is over 'n' intervals the same, the cropping is
used.
softhddevice.AutoCrop.Tolerance = 0
if detected crop area is too small, cut max 'n' pixels at top and
bottom.
softhddevice.Suspend.Close = 0
1 suspend closes x11 window, connection and audio device.
(use svdrpsend plug softhddevice RESU to resume, if you have no lirc)
softhddevice.Suspend.X11 = 0
1 suspend stops X11 server (not working yet)
Setup: /etc/vdr/remote.conf
------
@@ -144,7 +173,7 @@ Setup: /etc/vdr/remote.conf
Commandline:
------------
Use vdr -h to see the command line arguments support by the plugin.
Use vdr -h to see the command line arguments supported by the plugin.
-a audio_device
@@ -155,6 +184,12 @@ Commandline:
other to use alsa audio module (if compiled with alsa
support)
SVDRP:
------
Use 'svdrpsend.pl plug softhddevice HELP' to see the SVDRP commands
help and which are supported by the plugin.
Running:
--------

39
Todo
View File

@@ -21,48 +21,61 @@ $Id: $
missing:
software deinterlace (yadif, ...)
software decoder with software deinterlace
auto crop
zoom/fit-zoom 4:3 (SetVideoDisplayFormat, SetVideoFormat?)
ITU BT601, ITU BT709 (HD), RGB studio levels (16-235)?
suspend output / energie saver: stop audio, stop video, configurable
suspend output / energie saver: stop and restart X11
Option deinterlace off / deinterlace force!
Make output drivers better modular.
Make output drivers better modular (under construction).
crash:
AudioPlayHandlerThread -> pthread_cond_wait
video:
subtitle not cleared
subtitle could be asyncron
reduce warnings after channel switch
grab image with hardware and better scaling support
suspendoutput didn't show logo or black pictures
(must detect video format to show image)
hard channel switch
skip line not configurable from setup menu.
vdpau:
VdpPreemptionCallback handling
hard channel switch
suspendoutput didn't show logo or black picture.
libva:
hard channel switch
yaepghd (VaapiSetOutputPosition) support
can associate ony displayed part of osd
can associate only displayed part of osd
grab image for va-api
still many:
[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-intel-driver:
intel still has hangups most with 1080i
1080i does no v-sync (workaround written)
1080i does no v-sync (sometimes correct working with vaapi-ext)
OSD has sometimes wrong size (workaround written)
software decoder needs UV swab
libva-vdpau-driver:
G210/GT520 OSD update too slow (needs hardware problem workaround)
hangup on exit (VaapiDelDecoder -> VaapiCleanup
-> vaDestroyContext -> pthread_rwlock_wrlock)
with auto-crop OSD has wrong position
libva-xvba-driver:
with auto-crop OSD has wrong position
x11:
disable screensaver
audio:
write TS -> PES parser, which feeds audio before the next start packet
CodecAudioOpen can fail "can't open audio codec" and does Fatal exit.
Combine alsa+oss ringbuffer code.
Make alsa thread/polled and oss thread/polled output module runtime
selectable.
software volume support
audio/alsa:
better downmix of >2 channels on 2 channel hardware
@@ -77,7 +90,6 @@ audio/oss:
HDMI/SPDIF Passthrough:
only AC-3 written
Channels are wrong setup, if changing setting during operation.
split pcm and ac-3 out into two devices
support oss pass-through
playback of recording
@@ -91,11 +103,11 @@ setup:
Setup 4:3 zoom type
Some setup parameters are not used until restart.
Can a notice be added to the setup menu?
576i, 720p, fake 1080i, 1080i
unsorted:
Menu -> Setup -> Plugins -> skingenigmang -> General
-> Try 8bpp single area: no, has missing parts.
stoping vdr while plugin is suspended opens and closes a window.
future features (not planed for 1.0 - 1.5)
@@ -105,5 +117,6 @@ future features (not planed for 1.0 - 1.5)
software decoder for xv / opengl
atmolight support
multistream handling
pip support
upmix stereo to AC-3

203
audio.c
View File

@@ -99,7 +99,7 @@
//----------------------------------------------------------------------------
/**
** Audio output module typedef.
** Audio output module structure and typedef.
*/
typedef struct _audio_module_
{
@@ -112,18 +112,23 @@ typedef struct _audio_module_
int (*FreeBytes) (void); ///< number of bytes free in buffer
uint64_t(*GetDelay) (void); ///< get current audio delay
void (*SetVolume) (int); ///< set output volume
int (*Setup) (int *, int *); ///< setup channels, samplerate
int (*Setup) (int *, int *, int); ///< setup channels, samplerate
void (*Init) (void); ///< initialize audio output module
void (*Exit) (void); ///< cleanup audio output module
} AudioModule;
static const AudioModule NoopModule; ///< forward definition of noop module
//----------------------------------------------------------------------------
// Variables
//----------------------------------------------------------------------------
static const char *AudioModuleName; ///< which audio module to use
static const AudioModule *UsedAudioModule; ///< Selected audio module.
/// Selected audio module.
static const AudioModule *AudioUsedModule = &NoopModule;
static const char *AudioPCMDevice; ///< alsa/OSS PCM device name
static const char *AudioAC3Device; ///< alsa/OSS AC3 device name
static const char *AudioMixerDevice; ///< alsa/OSS mixer device name
static const char *AudioMixerChannel; ///< alsa/OSS mixer channel name
static volatile char AudioRunning; ///< thread running / stopped
@@ -132,6 +137,7 @@ static unsigned AudioSampleRate; ///< audio sample rate in hz
static unsigned AudioChannels; ///< number of audio channels
static const int AudioBytesProSample = 2; ///< number of bytes per sample
static int64_t AudioPTS; ///< audio pts clock
static const int AudioBufferTime = 450; ///< audio buffer time in ms
#ifdef USE_AUDIO_THREAD
static pthread_t AudioThread; ///< audio play thread
@@ -570,6 +576,8 @@ static void AlsaEnqueue(const void *samples, int count)
// direct play produces underuns on some hardware
#ifndef USE_AUDIO_THREAD
/**
** Place samples in audio output queue.
**
@@ -583,6 +591,8 @@ static void AlsaEnqueue(const void *samples, int count)
}
}
#endif
#ifdef USE_AUDIO_THREAD
//----------------------------------------------------------------------------
@@ -687,18 +697,25 @@ static void AlsaThreadFlushBuffers(void)
/**
** Open alsa pcm device.
**
** @param use_ac3 use ac3/pass-through device
*/
static snd_pcm_t *AlsaOpenPCM(void)
static snd_pcm_t *AlsaOpenPCM(int use_ac3)
{
const char *device;
snd_pcm_t *handle;
int err;
if (!(device = AudioPCMDevice)) {
if (!(device = getenv("ALSA_DEVICE"))) {
device = "default";
}
// &&|| hell
if (!(use_ac3 && ((device = AudioAC3Device)
|| (device = getenv("ALSA_AC3_DEVICE"))
|| (device = getenv("ALSA_PASSTHROUGH_DEVICE"))))
&& !(device = AudioPCMDevice) && !(device = getenv("ALSA_DEVICE"))) {
device = "default";
}
Debug(3, "audio/alsa: &&|| hell '%s'\n", device);
// open none blocking; if device is already used, we don't want wait
if ((err =
snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK,
SND_PCM_NONBLOCK)) < 0) {
@@ -725,7 +742,7 @@ static void AlsaInitPCM(void)
int err;
snd_pcm_uframes_t buffer_size;
if (!(handle = AlsaOpenPCM())) {
if (!(handle = AlsaOpenPCM(0))) {
return;
}
@@ -869,6 +886,7 @@ static uint64_t AlsaGetDelay(void)
**
** @param freq sample frequency
** @param channels number of channels
** @param use_ac3 use ac3/pass-through device
**
** @retval 0 everything ok
** @retval 1 didn't support frequency/channels combination
@@ -876,7 +894,7 @@ static uint64_t AlsaGetDelay(void)
**
** @todo audio changes must be queued and done when the buffer is empty
*/
static int AlsaSetup(int *freq, int *channels)
static int AlsaSetup(int *freq, int *channels, int use_ac3)
{
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
@@ -895,7 +913,7 @@ static int AlsaSetup(int *freq, int *channels)
handle = AlsaPCMHandle;
AlsaPCMHandle = NULL;
snd_pcm_close(handle);
if (!(handle = AlsaOpenPCM())) {
if (!(handle = AlsaOpenPCM(use_ac3))) {
return -1;
}
AlsaPCMHandle = handle;
@@ -1066,9 +1084,12 @@ static int AlsaSetup(int *freq, int *channels)
snd_pcm_state_name(snd_pcm_state(AlsaPCMHandle)));
AlsaStartThreshold = snd_pcm_frames_to_bytes(AlsaPCMHandle, period_size);
// min 333ms
if (AlsaStartThreshold < (*freq * *channels * AudioBytesProSample) / 3U) {
AlsaStartThreshold = (*freq * *channels * AudioBytesProSample) / 3U;
// buffer time/delay in ms
if (AlsaStartThreshold <
(*freq * *channels * AudioBytesProSample * AudioBufferTime) / 1000U) {
AlsaStartThreshold =
(*freq * *channels * AudioBytesProSample * AudioBufferTime) /
1000U;
}
// no bigger, than the buffer
if (AlsaStartThreshold > RingBufferFreeBytes(AlsaRingBuffer)) {
@@ -1127,6 +1148,7 @@ static void AlsaExit(void)
RingBufferDel(AlsaRingBuffer);
AlsaRingBuffer = NULL;
}
AlsaFlushBuffer = 0;
}
/**
@@ -1282,6 +1304,8 @@ static void OssFlushBuffers(void)
// OSS pcm polled
//----------------------------------------------------------------------------
#ifndef USE_AUDIO_THREAD
/**
** Place samples in audio output queue.
**
@@ -1308,6 +1332,8 @@ static void OssEnqueue(const void *samples, int count)
}
}
#endif
/**
** Play all samples possible, without blocking.
*/
@@ -1416,6 +1442,32 @@ static void OssThreadFlushBuffers(void)
//----------------------------------------------------------------------------
/**
** Open OSS pcm device.
**
** @param use_ac3 use ac3/pass-through device
*/
static int OssOpenPCM(int use_ac3)
{
const char *device;
int fildes;
// &&|| hell
if (!(use_ac3 && ((device = AudioAC3Device)
|| (device = getenv("OSS_AC3_AUDIODEV"))))
&& !(device = AudioPCMDevice) && !(device = getenv("OSS_AUDIODEV"))) {
device = "/dev/dsp";
}
Debug(3, "audio/oss: &&|| hell '%s'\n", device);
if ((fildes = open(device, O_WRONLY)) < 0) {
Error(_("audio/oss: can't open dsp device '%s': %s\n"), device,
strerror(errno));
return -1;
}
return fildes;
}
/**
** Initialize OSS pcm device.
**
@@ -1423,19 +1475,9 @@ static void OssThreadFlushBuffers(void)
*/
static void OssInitPCM(void)
{
const char *device;
int fildes;
if (!(device = AudioPCMDevice)) {
if (!(device = getenv("OSS_AUDIODEV"))) {
device = "/dev/dsp";
}
}
if ((fildes = open(device, O_WRONLY)) < 0) {
Error(_("audio/oss: can't open dsp device '%s': %s\n"), device,
strerror(errno));
return;
}
fildes = OssOpenPCM(0);
OssPcmFildes = fildes;
}
@@ -1568,6 +1610,7 @@ static uint64_t OssGetDelay(void)
**
** @param freq sample frequency
** @param channels number of channels
** @param use_ac3 use ac3/pass-through device
**
** @retval 0 everything ok
** @retval 1 didn't support frequency/channels combination
@@ -1575,7 +1618,7 @@ static uint64_t OssGetDelay(void)
**
** @todo audio changes must be queued and done when the buffer is empty
*/
static int OssSetup(int *freq, int *channels)
static int OssSetup(int *freq, int *channels, int use_ac3)
{
int ret;
int tmp;
@@ -1586,6 +1629,18 @@ static int OssSetup(int *freq, int *channels)
// flush any buffered data
AudioFlushBuffers();
if (1) { // close+open for pcm / ac3
int fildes;
fildes = OssPcmFildes;
OssPcmFildes = -1;
close(fildes);
if (!(fildes = OssOpenPCM(use_ac3))) {
return -1;
}
OssPcmFildes = fildes;
}
ret = 0;
tmp = AFMT_S16_NE; // native 16 bits
@@ -1652,9 +1707,13 @@ static int OssSetup(int *freq, int *channels)
}
// start when enough bytes for initial write
OssStartThreshold = bi.bytes + tmp;
// min 333ms
if (OssStartThreshold < (*freq * *channels * AudioBytesProSample) / 3U) {
OssStartThreshold = (*freq * *channels * AudioBytesProSample) / 3U;
// buffer time/delay in ms
if (OssStartThreshold <
(*freq * *channels * AudioBytesProSample * AudioBufferTime) /
1000U) {
OssStartThreshold =
(*freq * *channels * AudioBytesProSample * AudioBufferTime) /
1000U;
}
// no bigger, than the buffer
if (OssStartThreshold > RingBufferFreeBytes(OssRingBuffer)) {
@@ -1692,6 +1751,7 @@ static void OssExit(void)
close(OssMixerFildes);
OssMixerFildes = -1;
}
OssFlushBuffer = 0;
}
/**
@@ -1770,7 +1830,8 @@ static void NoopSetVolume( __attribute__ ((unused))
*/
static int NoopSetup( __attribute__ ((unused))
int *channels, __attribute__ ((unused))
int *freq)
int *freq, __attribute__ ((unused))
int use_ac3)
{
return -1;
}
@@ -1847,23 +1908,12 @@ static void *AudioPlayHandlerThread(void *dummy)
}
Debug(3, "audio: thread channels %d sample-rate %d hz\n",
AudioChannels, AudioSampleRate);
if (1) {
int16_t buf[6144 / 2];
buf[0] = htole16(0xF872); // iec 61937 sync word
buf[1] = htole16(0x4E1F);
buf[2] = htole16((7 << 5) << 8 | 0x00);
buf[3] = htole16(0x0000);
memset(buf + 4, 0, 6144 - 8);
AlsaEnqueue(buf, 6144);
}
#endif
}
#endif
Debug(3, "audio: play start\n");
UsedAudioModule->Thread();
AudioUsedModule->Thread();
}
return dummy;
@@ -1899,6 +1949,7 @@ static void AudioExitThread(void)
}
pthread_cond_destroy(&AudioStartCond);
pthread_mutex_destroy(&AudioMutex);
AudioThread = 0;
}
}
@@ -1928,7 +1979,7 @@ static const AudioModule *AudioModules[] = {
*/
void AudioEnqueue(const void *samples, int count)
{
UsedAudioModule->Enqueue(samples, count);
AudioUsedModule->Enqueue(samples, count);
}
/**
@@ -1936,7 +1987,7 @@ void AudioEnqueue(const void *samples, int count)
*/
void AudioFlushBuffers(void)
{
UsedAudioModule->FlushBuffers();
AudioUsedModule->FlushBuffers();
}
/**
@@ -1944,7 +1995,7 @@ void AudioFlushBuffers(void)
*/
void AudioPoller(void)
{
UsedAudioModule->Poller();
AudioUsedModule->Poller();
}
/**
@@ -1952,7 +2003,7 @@ void AudioPoller(void)
*/
int AudioFreeBytes(void)
{
return UsedAudioModule->FreeBytes();
return AudioUsedModule->FreeBytes();
}
/**
@@ -1962,7 +2013,7 @@ int AudioFreeBytes(void)
*/
uint64_t AudioGetDelay(void)
{
return UsedAudioModule->GetDelay();
return AudioUsedModule->GetDelay();
}
/**
@@ -2020,6 +2071,7 @@ void AudioSetVolume(int volume)
**
** @param freq sample frequency
** @param channels number of channels
** @param use_ac3 use ac3/pass-through device
**
** @retval 0 everything ok
** @retval 1 didn't support frequency/channels combination
@@ -2027,9 +2079,10 @@ void AudioSetVolume(int volume)
**
** @todo audio changes must be queued and done when the buffer is empty
*/
int AudioSetup(int *freq, int *channels)
int AudioSetup(int *freq, int *channels, int use_ac3)
{
Debug(3, "audio: channels %d frequency %d hz\n", *channels, *freq);
Debug(3, "audio: channels %d frequency %d hz %s\n", *channels, *freq,
use_ac3 ? "ac3" : "pcm");
// invalid parameter
if (!freq || !channels || !*freq || !*channels) {
@@ -2039,9 +2092,9 @@ int AudioSetup(int *freq, int *channels)
}
#ifdef USE_AUDIORING
// FIXME: need to store possible combination and report this
return AudioRingAdd(*freq, *channels);
return AudioRingAdd(*freq, *channels, use_ac3);
#endif
return UsedAudioModule->Setup(freq, channels);
return AudioUsedModule->Setup(freq, channels, use_ac3);
}
/**
@@ -2053,15 +2106,37 @@ int AudioSetup(int *freq, int *channels)
*/
void AudioSetDevice(const char *device)
{
AudioModuleName = "alsa"; // detect alsa/OSS
if (!device[0]) {
AudioModuleName = "noop";
} else if (device[0] == '/') {
AudioModuleName = "oss";
if (!AudioModuleName) {
AudioModuleName = "alsa"; // detect alsa/OSS
if (!device[0]) {
AudioModuleName = "noop";
} else if (device[0] == '/') {
AudioModuleName = "oss";
}
}
AudioPCMDevice = device;
}
/**
** Set pass-through audio device.
**
** @param device name of pass-through device (fe. "hw:0,1")
**
** @note this is currently usable with alsa only.
*/
void AudioSetDeviceAC3(const char *device)
{
if (!AudioModuleName) {
AudioModuleName = "alsa"; // detect alsa/OSS
if (!device[0]) {
AudioModuleName = "noop";
} else if (device[0] == '/') {
AudioModuleName = "oss";
}
}
AudioAC3Device = device;
}
/**
** Initialize audio output module.
**
@@ -2089,27 +2164,27 @@ void AudioInit(void)
//
for (u = 0; u < sizeof(AudioModules) / sizeof(*AudioModules); ++u) {
if (!strcasecmp(name, AudioModules[u]->Name)) {
UsedAudioModule = AudioModules[u];
Info(_("audio: '%s' output module used\n"), UsedAudioModule->Name);
AudioUsedModule = AudioModules[u];
Info(_("audio: '%s' output module used\n"), AudioUsedModule->Name);
goto found;
}
}
Error(_("audio: '%s' output module isn't supported\n"), name);
UsedAudioModule = &NoopModule;
AudioUsedModule = &NoopModule;
return;
found:
#ifdef USE_AUDIORING
AudioRingInit();
#endif
UsedAudioModule->Init();
AudioUsedModule->Init();
freq = 48000;
chan = 2;
if (AudioSetup(&freq, &chan)) { // set default parameters
if (AudioSetup(&freq, &chan, 0)) { // set default parameters
Error(_("audio: can't do initial setup\n"));
}
#ifdef USE_AUDIO_THREAD
if (UsedAudioModule->Thread) { // supports threads
if (AudioUsedModule->Thread) { // supports threads
AudioInitThread();
}
#endif
@@ -2125,10 +2200,12 @@ void AudioExit(void)
#ifdef USE_AUDIO_THREAD
AudioExitThread();
#endif
UsedAudioModule->Exit();
AudioUsedModule->Exit();
AudioUsedModule = &NoopModule;
#ifdef USE_AUDIORING
AudioRingExit();
#endif
AudioRunning = 0;
}
#ifdef AUDIO_TEST

View File

@@ -37,12 +37,13 @@ extern uint64_t AudioGetDelay(void); ///< get current audio delay
extern void AudioSetClock(int64_t); ///< set audio clock base
extern int64_t AudioGetClock(); ///< get current audio clock
extern void AudioSetVolume(int); ///< set volume
extern int AudioSetup(int *, int *); ///< setup audio output
extern int AudioSetup(int *, int *, int); ///< setup audio output
//extern void AudioPlay(void); ///< play audio
//extern void AudioPause(void); ///< pause audio
extern void AudioSetDevice(const char *); ///< set PCM audio device
extern void AudioSetDeviceAC3(const char *); ///< set Passthrough device
extern void AudioInit(void); ///< setup audio module
extern void AudioExit(void); ///< cleanup and exit audio module

42
codec.c
View File

@@ -320,20 +320,30 @@ static void Codec_draw_horiz_band(AVCodecContext * video_ctx,
**
** @param hw_decoder video hardware decoder
**
** @returns private decoder pointer for audio/video decoder.
** @returns private decoder pointer for video decoder.
*/
VideoDecoder *CodecVideoNewDecoder(VideoHwDecoder * hw_decoder)
{
VideoDecoder *decoder;
if (!(decoder = calloc(1, sizeof(*decoder)))) {
Fatal(_("codec: Can't allocate vodeo decoder\n"));
Fatal(_("codec: can't allocate vodeo decoder\n"));
}
decoder->HwDecoder = hw_decoder;
return decoder;
}
/**
** Deallocate a video decoder context.
**
** @param decoder private video decoder
*/
void CodecVideoDelDecoder(VideoDecoder * decoder)
{
free(decoder);
}
/**
** Open video decoder.
**
@@ -347,6 +357,9 @@ void CodecVideoOpen(VideoDecoder * decoder, const char *name, int codec_id)
Debug(3, "codec: using codec %s or ID %#04x\n", name, codec_id);
if (decoder->VideoCtx) {
Error(_("codec: missing close\n"));
}
//
// ffmpeg compatibility hack
//
@@ -548,13 +561,13 @@ void CodecVideoDecode(VideoDecoder * decoder, const AVPacket * avpkt)
video_ctx->frame_number, used);
}
if (used != pkt->size) {
if (used >= 0) {
if (used >= 0 && used < pkt->size) {
// some tv channels, produce this
Debug(4,
"codec: ooops didn't use complete video packet used %d of %d\n",
used, pkt->size);
pkt->data += used;
pkt->size -= used;
pkt->data += used;
goto next_part;
}
Debug(3, "codec: bad frame %d\n", used);
@@ -613,21 +626,29 @@ static char CodecPassthroughAC3; ///< pass ac3 through
/**
** Allocate a new audio decoder context.
**
** @param hw_decoder video hardware decoder
**
** @returns private decoder pointer for audio/video decoder.
** @returns private decoder pointer for audio decoder.
*/
AudioDecoder *CodecAudioNewDecoder(void)
{
AudioDecoder *audio_decoder;
if (!(audio_decoder = calloc(1, sizeof(*audio_decoder)))) {
Fatal(_("codec: Can't allocate audio decoder\n"));
Fatal(_("codec: can't allocate audio decoder\n"));
}
return audio_decoder;
}
/**
** Deallocate an audio decoder context.
**
** @param decoder private audio decoder
*/
void CodecAudioDelDecoder(AudioDecoder * decoder)
{
free(decoder);
}
/**
** Open audio decoder.
**
@@ -794,6 +815,7 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
if (audio_decoder->SampleRate != audio_ctx->sample_rate
|| audio_decoder->Channels != audio_ctx->channels) {
int err;
int isAC3;
if (audio_decoder->ReSample) {
audio_resample_close(audio_decoder->ReSample);
@@ -807,16 +829,18 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
// SPDIF/HDMI passthrough
if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
audio_decoder->HwChannels = 2;
isAC3 = 1;
} else
#endif
{
audio_decoder->HwChannels = audio_ctx->channels;
isAC3 = 0;
}
// channels not support?
if ((err =
AudioSetup(&audio_decoder->HwSampleRate,
&audio_decoder->HwChannels))) {
&audio_decoder->HwChannels, isAC3))) {
Debug(3, "codec/audio: resample %dHz *%d -> %dHz *%d\n",
audio_ctx->sample_rate, audio_ctx->channels,
audio_decoder->HwSampleRate,

View File

@@ -40,6 +40,9 @@ typedef struct _audio_decoder_ AudioDecoder;
/// Allocate a new video decoder context.
extern VideoDecoder *CodecVideoNewDecoder(VideoHwDecoder *);
/// Deallocate a video decoder context.
extern void CodecVideoDelDecoder(VideoDecoder *);
/// Open video codec.
extern void CodecVideoOpen(VideoDecoder *, const char *, int);
@@ -55,6 +58,9 @@ extern void CodecVideoFlushBuffers(VideoDecoder *);
/// Allocate a new audio decoder context.
extern AudioDecoder *CodecAudioNewDecoder(void);
/// Deallocate an audio decoder context.
extern void CodecAudioDelDecoder(AudioDecoder *);
/// Open audio codec.
extern void CodecAudioOpen(AudioDecoder *, const char *, int);

2
misc.h
View File

@@ -86,7 +86,7 @@ static inline void Syslog(const int level, const char *format, ...)
/**
** Show fatal error.
*/
#define Fatal(fmt...) do { Error(fmt); exit(-1); } while (0)
#define Fatal(fmt...) do { Error(fmt); abort(); } while (0)
/**
** Show warning.

View File

@@ -39,6 +39,9 @@
#define __USE_GNU
#endif
#include <pthread.h>
#ifdef USE_JPEG
#include <jpeglib.h>
#endif
#include "misc.h"
#include "softhddev.h"
@@ -58,8 +61,6 @@ static char ConfigVdpauDecoder = 1; ///< use vdpau decoder, if possible
#endif
static char ConfigFullscreen; ///< fullscreen modus
static char ConfigSuspendClose = 1; ///< suspend should close devices
static char ConfigSuspendX11 = 1; ///< suspend should stop x11
static pthread_mutex_t SuspendLockMutex; ///< suspend lock mutex
@@ -123,6 +124,8 @@ static const uint16_t SampleRateTable[4] = {
** FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4
** Layer II & III:
** FrameLengthInBytes = 144 * BitRate / SampleRate + Padding
**
** @todo sometimes detects wrong position
*/
static int FindAudioSync(const AVPacket * avpkt)
{
@@ -209,15 +212,15 @@ int PlayAudio(const uint8_t * data, int size,
if (VideoFreezed) { // video freezed
return 0;
}
if (SkipAudio || !MyAudioDecoder) { // skip audio
return size;
}
if (NewAudioStream) {
// FIXME: does this clear the audio ringbuffer?
CodecAudioClose(MyAudioDecoder);
AudioCodecID = CODEC_ID_NONE;
NewAudioStream = 0;
}
if (SkipAudio) { // skip audio
return size;
}
// PES header 0x00 0x00 0x01 ID
// ID 0xBD 0xC0-0xCF
@@ -260,27 +263,17 @@ int PlayAudio(const uint8_t * data, int size,
// Syncword - 0x0B77
if (data[0] == 0x0B && data[1] == 0x77) {
if (!MyAudioDecoder) {
MyAudioDecoder = CodecAudioNewDecoder();
AudioCodecID = CODEC_ID_NONE;
}
if (AudioCodecID != CODEC_ID_AC3) {
Debug(3, "[softhddev]%s: AC-3 %d\n", __FUNCTION__, id);
CodecAudioClose(MyAudioDecoder);
CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_AC3);
AudioCodecID = CODEC_ID_AC3;
}
// Syncword - 0xFFFC - 0xFFFF
} else if (data[0] == 0xFF && (data[1] & 0xFC) == 0xFC) {
if (!MyAudioDecoder) {
MyAudioDecoder = CodecAudioNewDecoder();
AudioCodecID = CODEC_ID_NONE;
}
if (AudioCodecID != CODEC_ID_MP2) {
Debug(3, "[softhddev]%s: MP2 %d\n", __FUNCTION__, id);
CodecAudioClose(MyAudioDecoder);
CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_MP2);
AudioCodecID = CODEC_ID_MP2;
}
@@ -297,20 +290,17 @@ int PlayAudio(const uint8_t * data, int size,
if (n < 0) {
return osize;
}
if (!MyAudioDecoder) {
MyAudioDecoder = CodecAudioNewDecoder();
}
avpkt->pts = AV_NOPTS_VALUE;
CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_MP2);
AudioCodecID = CODEC_ID_MP2;
data += n;
size -= n;
}
}
// no decoder or codec known
if (!MyAudioDecoder || AudioCodecID == CODEC_ID_NONE) {
return osize;
// no decoder or codec known
if (AudioCodecID == CODEC_ID_NONE) {
return osize;
}
}
avpkt->data = (void *)data;
@@ -347,7 +337,8 @@ void SetVolumeDevice(int volume)
#include <alsa/iatomic.h> // portable atomic_t
uint32_t VideoSwitch; ///< debug video switch ticks
static volatile char NewVideoStream; ///< new video stream
static volatile char NewVideoStream; ///< flag new video stream
static VideoHwDecoder *MyHwDecoder; ///< video hw decoder
static VideoDecoder *MyVideoDecoder; ///< video decoder
static enum CodecID VideoCodecID; ///< current codec id
@@ -361,7 +352,7 @@ static volatile char Usr1Signal; ///< true got usr1 signal
static AVPacket VideoPacketRb[VIDEO_PACKET_MAX];
static int VideoPacketWrite; ///< write pointer
static int VideoPacketRead; ///< read pointer
static atomic_t VideoPacketsFilled; ///< how many of the buffer is used
atomic_t VideoPacketsFilled; ///< how many of the buffer is used
static volatile char VideoClearBuffers; ///< clear video buffers
static volatile char SkipVideo; ///< skip video
@@ -388,6 +379,7 @@ static void VideoPacketInit(void)
}
atomic_set(&VideoPacketsFilled, 0);
VideoPacketRead = VideoPacketWrite = 0;
}
/**
@@ -460,10 +452,10 @@ static void VideoNextPacket(int codec_id)
avpkt = &VideoPacketRb[VideoPacketWrite];
if (!avpkt->stream_index) { // ignore empty packets
if (codec_id == CODEC_ID_NONE) {
Debug(3, "video: possible stream change loss\n");
if (codec_id != CODEC_ID_NONE) {
return;
}
return;
Debug(3, "video: possible stream change loss\n");
}
if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 1) {
@@ -477,6 +469,7 @@ static void VideoNextPacket(int codec_id)
}
// clear area for decoder, always enough space allocated
memset(avpkt->data + avpkt->stream_index, 0, FF_INPUT_BUFFER_PADDING_SIZE);
avpkt->priv = (void *)(size_t) codec_id;
// advance packet write
@@ -539,8 +532,8 @@ int VideoDecode(void)
CodecVideoClose(MyVideoDecoder);
goto skip;
}
// size can be zero
goto skip;
break;
case CODEC_ID_MPEG2VIDEO:
if (last_codec_id != CODEC_ID_MPEG2VIDEO) {
last_codec_id = CODEC_ID_MPEG2VIDEO;
@@ -592,16 +585,36 @@ static void StartVideo(void)
}
VideoOsdInit();
if (!MyVideoDecoder) {
VideoHwDecoder *hw_decoder;
if ((hw_decoder = VideoNewHwDecoder())) {
MyVideoDecoder = CodecVideoNewDecoder(hw_decoder);
if ((MyHwDecoder = VideoNewHwDecoder())) {
MyVideoDecoder = CodecVideoNewDecoder(MyHwDecoder);
}
VideoCodecID = CODEC_ID_NONE;
}
VideoPacketInit();
}
/**
** Stop video.
*/
static void StopVideo(void)
{
VideoOsdExit();
VideoExit();
if (MyVideoDecoder) {
// FIXME: this can crash, hw decoder released by video exit
CodecVideoClose(MyVideoDecoder);
CodecVideoDelDecoder(MyVideoDecoder);
MyVideoDecoder = NULL;
}
if (MyHwDecoder) {
// done by exit: VideoDelHwDecoder(MyHwDecoder);
MyHwDecoder = NULL;
}
VideoPacketExit();
NewVideoStream = 1;
}
#ifdef DEBUG
/**
@@ -732,13 +745,15 @@ int PlayVideo(const uint8_t * data, int size)
}
#endif
}
// FIXME: no valid mpeg2/h264 detection yet
check = data + 9 + n;
if (0) {
printf("%02x: %02x %02x %02x %02x %02x\n", data[6], check[0], check[1],
check[2], check[3], check[4]);
}
// 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)) {
@@ -778,10 +793,15 @@ int PlayVideo(const uint8_t * data, int size)
Debug(3, "video: not detected\n");
return size;
}
if (VideoCodecID == CODEC_ID_MPEG2VIDEO) {
// 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;
}
}
@@ -791,6 +811,82 @@ int PlayVideo(const uint8_t * data, int size)
return size;
}
#ifdef USE_JPEG
uint8_t *CreateJpeg(uint8_t * image, int raw_size, int *size, int quality,
int width, int height)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_ptr[1];
int row_stride;
uint8_t *outbuf;
long unsigned int outsize;
outbuf = NULL;
outsize = 0;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_mem_dest(&cinfo, &outbuf, &outsize);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = raw_size / height / width;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE);
jpeg_start_compress(&cinfo, TRUE);
row_stride = width * 3;
while (cinfo.next_scanline < cinfo.image_height) {
row_ptr[0] = &image[cinfo.next_scanline * row_stride];
jpeg_write_scanlines(&cinfo, row_ptr, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
*size = outsize;
return outbuf;
}
#endif
/**
** Grabs the currently visible screen image.
**
** @param size size of the returned data
** @param jpeg flag true, create JPEG data
** @param quality JPEG quality
** @param width number of horizontal pixels in the frame
** @param height number of vertical pixels in the frame
*/
uint8_t *GrabImage(int *size, int jpeg, int quality, int width, int height)
{
if (jpeg) {
#ifdef USE_JPEG
int raw_size;
uint8_t *image;
uint8_t *jpg_image;
raw_size = 0;
image = VideoGrab(&raw_size, &width, &height, 0);
jpg_image = CreateJpeg(image, raw_size, size, quality, width, height);
free(image);
return jpg_image;
#else
(void)quality;
Error(_("softhddev: jpeg grabbing not supported\n"));
return NULL;
#endif
}
if (width != -1 && height != -1) {
Warning(_("softhddev: scaling unsupported\n"));
}
return VideoGrab(size, &width, &height, 1);
}
//////////////////////////////////////////////////////////////////////////////
/**
@@ -858,18 +954,50 @@ void Freeze(void)
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 };
// must be a PES start code
if (size < 9 || !data || data[0] || data[1] || data[2] != 0x01) {
Error(_("[softhddev] invalid PES video packet\n"));
Error(_("[softhddev] invalid still video packet\n"));
return;
}
Clear(); // flush video buffers
if (VideoCodecID == CODEC_ID_NONE) {
// FIXME: should detect codec, see PlayVideo
Error(_("[softhddev] no codec known for still picture\n"));
return;
}
//Clear(); // flush video buffers
// +1 future for deinterlace
for (i = -1; i < (VideoCodecID == CODEC_ID_MPEG2VIDEO ? 3 : 17); ++i) {
PlayVideo(data, size); // reference frames
//if ( 1 ) {
const uint8_t *split;
int n;
// split the I-frame into single pes packets
split = data;
n = size;
do {
int len;
len = (split[4] << 8) + split[5];
if (len > n) {
break;
}
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) {
VideoEnqueue(AV_NOPTS_VALUE, seq_end_h264, sizeof(seq_end_h264));
} else {
VideoEnqueue(AV_NOPTS_VALUE, seq_end_mpeg, sizeof(seq_end_mpeg));
}
VideoNextPacket(VideoCodecID); // terminate last packet
}
VideoNextPacket(VideoCodecID); // terminate last packet
}
/**
@@ -946,7 +1074,6 @@ void OsdClose(void)
*/
void OsdDrawARGB(int x, int y, int height, int width, const uint8_t * argb)
{
Resume();
VideoOsdDrawARGB(x, y, height, width, argb);
}
@@ -960,6 +1087,7 @@ static char ConfigStartX11Server; ///< flag start the x11 server
const char *CommandLineHelp(void)
{
return " -a device\taudio device (fe. alsa: hw:0,0 oss: /dev/dsp)\n"
" -p device\taudio device (alsa only) for pass-through (hw:0,1)\n"
" -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"
@@ -978,10 +1106,13 @@ int ProcessArgs(int argc, char *const argv[])
// Parse arguments.
//
for (;;) {
switch (getopt(argc, argv, "-a:d:fg:x")) {
switch (getopt(argc, argv, "-a:p:d:fg:x")) {
case 'a': // audio device
AudioSetDevice(optarg);
continue;
case 'p': // pass-through audio device
AudioSetDeviceAC3(optarg);
continue;
case 'd': // x11 display name
X11DisplayName = optarg;
continue;
@@ -1117,22 +1248,18 @@ static void StartXServer(void)
*/
void SoftHdDeviceExit(void)
{
// lets hope that vdr does a good thead cleanup
// no it doesn't do a good thread cleanup
if (MyVideoDecoder) {
CodecVideoClose(MyVideoDecoder);
// FIXME: CodecDelVideoDecoder(MyVideoDecoder);
MyVideoDecoder = NULL;
}
// lets hope that vdr does a good thread cleanup
AudioExit();
if (MyAudioDecoder) {
CodecAudioClose(MyAudioDecoder);
// FIXME: CodecDelAudioDecoder(MyAudioDecoder);
CodecAudioDelDecoder(MyAudioDecoder);
MyAudioDecoder = NULL;
}
NewAudioStream = 0;
StopVideo();
VideoOsdExit();
VideoExit();
AudioExit();
CodecExit();
VideoPacketExit();
@@ -1140,8 +1267,34 @@ void SoftHdDeviceExit(void)
Debug(3, "x-setup: Stop x11 server\n");
if (X11ServerPid) {
int waittime;
int timeout;
pid_t wpid;
int status;
kill(X11ServerPid, SIGTERM);
// FIXME: wait for x11 finishing
waittime = 0;
timeout = 500; // 0.5s
// wait for x11 finishing, with timeout
do {
wpid = waitpid(X11ServerPid, &status, WNOHANG);
if (wpid) {
break;
}
if (waittime++ < timeout) {
usleep(1 * 1000);
continue;
}
kill(X11ServerPid, SIGKILL);
} while (waittime < timeout);
if (wpid && WIFEXITED(status)) {
Debug(3, "x-setup: x11 server exited (%d)\n",
WEXITSTATUS(status));
}
if (wpid && WIFSIGNALED(status)) {
Debug(3, "x-setup: x11 server killed (%d)\n",
WTERMSIG(status));
}
}
}
@@ -1157,8 +1310,12 @@ void Start(void)
StartXServer();
}
CodecInit();
// FIXME: AudioInit for HDMI after X11 startup
AudioInit();
MyAudioDecoder = CodecAudioNewDecoder();
AudioCodecID = CODEC_ID_NONE;
if (!ConfigStartX11Server) {
StartVideo();
}
@@ -1191,8 +1348,12 @@ void MainThreadHook(void)
/**
** Suspend plugin.
**
** @param video suspend closes video
** @param audio suspend closes audio
** @param dox11 suspend closes x11 server
*/
void Suspend(void)
void Suspend(int video, int audio, int dox11)
{
pthread_mutex_lock(&SuspendLockMutex);
if (SkipVideo && SkipAudio) { // already suspended
@@ -1206,13 +1367,25 @@ void Suspend(void)
SkipAudio = 1;
pthread_mutex_unlock(&SuspendLockMutex);
if (ConfigSuspendClose) {
if (audio || video) {
pthread_mutex_lock(&SuspendLockMutex);
// FIXME: close audio
// FIXME: close video
if (audio) {
AudioExit();
if (MyAudioDecoder) {
CodecAudioClose(MyAudioDecoder);
CodecAudioDelDecoder(MyAudioDecoder);
MyAudioDecoder = NULL;
}
NewAudioStream = 0;
}
if (video) {
StopVideo();
}
pthread_mutex_unlock(&SuspendLockMutex);
}
if (ConfigSuspendX11) {
if (dox11) {
// FIXME: stop x11, if started
}
}
@@ -1228,13 +1401,20 @@ void Resume(void)
Debug(3, "[softhddev]%s:\n", __FUNCTION__);
if (ConfigSuspendX11) {
pthread_mutex_lock(&SuspendLockMutex);
// FIXME: start x11
if (!MyHwDecoder) { // video not running
StartVideo();
}
if (ConfigSuspendClose) {
pthread_mutex_lock(&SuspendLockMutex);
pthread_mutex_unlock(&SuspendLockMutex);
if (!MyAudioDecoder) { // audio not running
AudioInit();
MyAudioDecoder = CodecAudioNewDecoder();
AudioCodecID = CODEC_ID_NONE;
}
SkipVideo = 0;
SkipAudio = 0;
pthread_mutex_unlock(&SuspendLockMutex);
}

View File

@@ -46,6 +46,8 @@ extern "C"
extern int PlayVideo(const uint8_t *, int);
/// C plugin play TS video packet
extern void PlayTsVideo(const uint8_t *, int);
/// C plugin grab an image
extern uint8_t *GrabImage(int *, int, int, int, int);
/// C plugin set play mode
extern void SetPlayMode(void);
@@ -77,7 +79,7 @@ extern "C"
extern void MainThreadHook(void);
/// Suspend plugin
extern void Suspend(void);
extern void Suspend(int, int, int);
/// Resume plugin
extern void Resume(void);
#ifdef __cplusplus

View File

@@ -42,7 +42,7 @@ extern "C"
//////////////////////////////////////////////////////////////////////////////
static const char *const VERSION = "0.3.5";
static const char *const VERSION = "0.4.5";
static const char *const DESCRIPTION =
trNOOP("A software and GPU emulated HD device");
@@ -77,8 +77,16 @@ static int ConfigVideoSharpen[RESOLUTIONS];
static int ConfigVideoScaling[RESOLUTIONS];
static int ConfigVideoAudioDelay; ///< config audio delay
static int ConfigVideoSkipLines; ///< config skip lines top/bottom
static int ConfigAudioPassthrough; ///< config audio pass-through
static int ConfigAutoCropInterval; ///< auto crop detection interval
static int ConfigAutoCropDelay; ///< auto crop detection delay
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
//////////////////////////////////////////////////////////////////////////////
@@ -123,7 +131,7 @@ extern "C" void FeedKeyPress(const char *keymap, const char *key, int repeat,
csoft = new cSoftRemote(keymap);
}
dsyslog("[softhddev]%s %s, %s\n", __FUNCTION__, keymap, key);
//dsyslog("[softhddev]%s %s, %s\n", __FUNCTION__, keymap, key);
csoft->Put(key, repeat, release);
}
@@ -145,12 +153,13 @@ class cSoftOsd:public cOsd
cSoftOsd::cSoftOsd(int left, int top, uint level)
:cOsd(left, top, level)
{
// FIXME: OsdWidth/OsdHeight not correct!
dsyslog("[softhddev]%s: %dx%d+%d+%d, %d\n", __FUNCTION__, OsdWidth(),
OsdHeight(), left, top, level);
/* FIXME: OsdWidth/OsdHeight not correct!
dsyslog("[softhddev]%s: %dx%d+%d+%d, %d\n", __FUNCTION__, OsdWidth(),
OsdHeight(), left, top, level);
*/
this->Level = level;
//SetActive(true);
SetActive(true);
}
cSoftOsd::~cSoftOsd(void)
@@ -292,8 +301,8 @@ void cSoftOsd::Flush(void)
h = pm->ViewPort().Height();
/*
dsyslog("[softhddev]%s: draw %dx%d+%d+%d %p\n", __FUNCTION__, w, h, x,
y, pm->Data());
dsyslog("[softhddev]%s: draw %dx%d+%d+%d %p\n", __FUNCTION__, w, h,
x, y, pm->Data());
*/
OsdDrawARGB(x, y, w, h, pm->Data());
@@ -362,6 +371,11 @@ class cMenuSetupSoft:public cMenuSetupPage
int Sharpen[RESOLUTIONS];
int AudioDelay;
int AudioPassthrough;
int AutoCropInterval;
int AutoCropDelay;
int AutoCropTolerance;
int SuspendClose;
int SuspendX11;
protected:
virtual void Store(void);
public:
@@ -370,6 +384,8 @@ class cMenuSetupSoft:public cMenuSetupPage
/**
** Create a seperator item.
**
** @param label text inside separator
*/
static inline cOsdItem *SeparatorItem(const char *label)
{
@@ -439,6 +455,29 @@ cMenuSetupSoft::cMenuSetupSoft(void)
AudioPassthrough = ConfigAudioPassthrough;
Add(new cMenuEditStraItem(tr("Audio pass-through"), &AudioPassthrough, 2,
passthrough));
//
// auto-crop
//
Add(SeparatorItem(tr("Auto-crop")));
AutoCropInterval = ConfigAutoCropInterval;
Add(new cMenuEditIntItem(tr("autocrop interval (frames)"),
&AutoCropInterval, 0, 200));
AutoCropDelay = ConfigAutoCropDelay;
Add(new cMenuEditIntItem(tr("autocrop delay (n * interval)"),
&AutoCropDelay, 0, 200));
AutoCropTolerance = ConfigAutoCropTolerance;
Add(new cMenuEditIntItem(tr("autocrop tolerance (pixel)"),
&AutoCropTolerance, 0, 32));
//
// suspend
//
Add(SeparatorItem(tr("Suspend")));
SuspendClose = ConfigSuspendClose;
Add(new cMenuEditBoolItem(tr("suspend closes video+audio"), &SuspendClose,
trVDR("no"), trVDR("yes")));
SuspendX11 = ConfigSuspendX11;
Add(new cMenuEditBoolItem(tr("suspend stops x11"), &SuspendX11,
trVDR("no"), trVDR("yes")));
}
/**
@@ -478,6 +517,89 @@ void cMenuSetupSoft::Store(void)
VideoSetAudioDelay(ConfigVideoAudioDelay);
SetupStore("AudioPassthrough", ConfigAudioPassthrough = AudioPassthrough);
CodecSetAudioPassthrough(ConfigAudioPassthrough);
SetupStore("AutoCrop.Interval", ConfigAutoCropInterval = AutoCropInterval);
SetupStore("AutoCrop.Delay", ConfigAutoCropDelay = AutoCropDelay);
SetupStore("AutoCrop.Tolerance", ConfigAutoCropTolerance = AutoCropTolerance);
VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay, ConfigAutoCropTolerance);
SetupStore("Suspend.Close", ConfigSuspendClose = SuspendClose);
SetupStore("Suspend.X11", ConfigSuspendX11 = SuspendX11);
}
//////////////////////////////////////////////////////////////////////////////
// cPlayer
//////////////////////////////////////////////////////////////////////////////
/**
** Dummy player for suspend mode.
*/
class cSoftHdPlayer:public cPlayer
{
protected:
public:
cSoftHdPlayer(void);
virtual ~ cSoftHdPlayer();
};
cSoftHdPlayer::cSoftHdPlayer(void)
{
}
cSoftHdPlayer::~cSoftHdPlayer()
{
Detach();
}
//////////////////////////////////////////////////////////////////////////////
// cControl
//////////////////////////////////////////////////////////////////////////////
/**
** Dummy control for suspend mode.
*/
class cSoftHdControl:public cControl
{
private:
cSoftHdPlayer * Player;
public:
virtual void Hide(void)
{
}
virtual eOSState ProcessKey(eKeys);
cSoftHdControl(void);
virtual ~ cSoftHdControl();
};
eOSState cSoftHdControl::ProcessKey(eKeys key)
{
if (!ISMODELESSKEY(key) || key == kBack || key == kStop) {
if (Player) {
delete Player;
Player = NULL;
Resume();
}
return osEnd;
}
return osContinue;
}
cSoftHdControl::cSoftHdControl(void)
: cControl(Player = new cSoftHdPlayer)
{
}
cSoftHdControl::~cSoftHdControl()
{
if (Player) {
delete Player;
Player = NULL;
}
Resume();
}
//////////////////////////////////////////////////////////////////////////////
@@ -488,7 +610,7 @@ class cSoftHdDevice:public cDevice
{
public:
cSoftHdDevice(void);
virtual ~ cSoftHdDevice(void);
virtual ~ cSoftHdDevice(void);
virtual bool HasDecoder(void) const;
virtual bool CanReplay(void) const;
@@ -524,13 +646,13 @@ class cSoftHdDevice:public cDevice
#if 0
// SPU facilities
private:
cDvbSpuDecoder * spuDecoder;
cDvbSpuDecoder * spuDecoder;
public:
virtual cSpuDecoder * GetSpuDecoder(void);
virtual cSpuDecoder * GetSpuDecoder(void);
#endif
protected:
virtual void MakePrimaryDevice(bool);
virtual void MakePrimaryDevice(bool);
};
cSoftHdDevice::cSoftHdDevice(void)
@@ -581,16 +703,22 @@ bool cSoftHdDevice::HasDecoder(void) const
return true;
}
/**
** Returns true if this device can currently start a replay session.
*/
bool cSoftHdDevice::CanReplay(void) const
{
return true;
}
bool cSoftHdDevice::SetPlayMode(ePlayMode PlayMode)
/**
** Sets the device into the given play mode.
*/
bool cSoftHdDevice::SetPlayMode(ePlayMode play_mode)
{
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, PlayMode);
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, play_mode);
switch (PlayMode) {
switch (play_mode) {
case pmAudioVideo:
break;
case pmAudioOnly:
@@ -601,9 +729,11 @@ bool cSoftHdDevice::SetPlayMode(ePlayMode PlayMode)
case pmNone:
return true;
case pmExtern_THIS_SHOULD_BE_AVOIDED:
break;
dsyslog("[softhddev] play mode external\n");
Suspend(1, 1, 0);
return true;
default:
dsyslog("[softhddev]playmode not implemented... %d\n", PlayMode);
dsyslog("[softhddev]playmode not implemented... %d\n", play_mode);
break;
}
::SetPlayMode();
@@ -612,7 +742,7 @@ bool cSoftHdDevice::SetPlayMode(ePlayMode PlayMode)
int64_t cSoftHdDevice::GetSTC(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
//dsyslog("[softhddev]%s:\n", __FUNCTION__);
return::VideoGetClock();
}
@@ -674,8 +804,8 @@ void cSoftHdDevice::SetVolumeDevice(int volume)
*/
void cSoftHdDevice::StillPicture(const uchar * data, int length)
{
dsyslog("[softhddev]%s: %s\n", __FUNCTION__,
data[0] == 0x47 ? "ts" : "pes");
dsyslog("[softhddev]%s: %s %p %d\n", __FUNCTION__,
data[0] == 0x47 ? "ts" : "pes", data, length);
if (data[0] == 0x47) { // ts sync
cDevice::StillPicture(data, length);
@@ -694,7 +824,7 @@ void cSoftHdDevice::StillPicture(const uchar * data, int length)
bool cSoftHdDevice::Poll(
__attribute__ ((unused)) cPoller & poller, int timeout_ms)
{
// dsyslog("[softhddev]%s: %d\n", __FUNCTION__, timeout_ms);
//dsyslog("[softhddev]%s: %d\n", __FUNCTION__, timeout_ms);
return::Poll(timeout_ms);
}
@@ -810,13 +940,22 @@ int cSoftHdDevice::PlayTsAudio(const uchar * data, int length)
#endif
uchar *cSoftHdDevice::GrabImage(int &size, bool jpeg, int quality, int sizex,
int sizey)
/**
** Grabs the currently visible screen image.
**
** @param size size of the returned data
** @param jpeg flag true, create JPEG data
** @param quality JPEG quality
** @param width number of horizontal pixels in the frame
** @param height number of vertical pixels in the frame
*/
uchar *cSoftHdDevice::GrabImage(int &size, bool jpeg, int quality, int width,
int height)
{
dsyslog("[softhddev]%s: %d, %d, %d, %dx%d\n", __FUNCTION__, size, jpeg,
quality, sizex, sizey);
quality, width, height);
return NULL;
return::GrabImage(&size, jpeg, quality, width, height);
}
//////////////////////////////////////////////////////////////////////////////
@@ -835,7 +974,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);
@@ -934,9 +1073,14 @@ void cPluginSoftHdDevice::Stop(void)
#if 0
/**
** Perform any cleanup or other regular tasks.
*/
void cPluginSoftHdDevice::Housekeeping(void)
{
// Perform any cleanup or other regular tasks.
dsyslog("[softhddev]%s:\n", __FUNCTION__);
// ::Housekeeping();
}
#endif
@@ -956,11 +1100,14 @@ const char *cPluginSoftHdDevice::MainMenuEntry(void)
*/
cOsdObject *cPluginSoftHdDevice::MainMenuAction(void)
{
dsyslog("[softhddev]%s:\n", __FUNCTION__);
//dsyslog("[softhddev]%s:\n", __FUNCTION__);
cDevice::PrimaryDevice()->StopReplay();
Suspend();
//MyDevice->StopReplay();
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
if (ShutdownHandler.GetUserInactiveTime()) {
dsyslog("[softhddev]%s: set user inactive\n", __FUNCTION__);
ShutdownHandler.SetUserInactive();
}
@@ -983,7 +1130,7 @@ void cPluginSoftHdDevice::MainThreadHook(void)
// check if user is inactive, automatic enter suspend mode
if (ShutdownHandler.IsUserInactive()) {
// this is regular called, but guarded against double calls
Suspend();
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
}
::MainThreadHook();
@@ -1001,11 +1148,15 @@ cMenuSetupPage *cPluginSoftHdDevice::SetupMenu(void)
/**
** Parse setup parameters
**
** @param name paramter name (case sensetive)
** @param value value as string
**
** @returns true if the parameter is supported.
*/
bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
{
int i;
char buf[128];
//dsyslog("[softhddev]%s: '%s' = '%s'\n", __FUNCTION__, name, value);
@@ -1018,6 +1169,8 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
return true;
}
for (i = 0; i < RESOLUTIONS; ++i) {
char buf[128];
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "Scaling");
if (!strcmp(name, buf)) {
ConfigVideoScaling[i] = atoi(value);
@@ -1050,6 +1203,11 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
return true;
}
}
if (!strcmp(name, "SkipLines")) {
VideoSetSkipLines(ConfigVideoSkipLines = atoi(value));
return true;
}
if (!strcmp(name, "AudioDelay")) {
VideoSetAudioDelay(ConfigVideoAudioDelay = atoi(value));
return true;
@@ -1059,6 +1217,29 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
return true;
}
if (!strcmp(name, "AutoCrop.Interval")) {
VideoSetAutoCrop(ConfigAutoCropInterval =
atoi(value), ConfigAutoCropDelay, ConfigAutoCropTolerance);
return true;
}
if (!strcmp(name, "AutoCrop.Delay")) {
VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay =
atoi(value), ConfigAutoCropTolerance);
return true;
}
if (!strcmp(name, "AutoCrop.Tolerance")) {
VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay, ConfigAutoCropTolerance = atoi(value));
return true;
}
if (!strcmp(name, "Suspend.Close")) {
ConfigSuspendClose = atoi(value);
return true;
}
if (!strcmp(name, "Suspend.X11")) {
ConfigSuspendX11 = atoi(value);
return true;
}
return false;
}
@@ -1087,8 +1268,10 @@ const char **cPluginSoftHdDevice::SVDRPHelpPages(void)
{
// FIXME: translation?
static const char *text[] = {
"SUSP\n",
" Suspend plugin",
"SUSP\n"
" Suspend plugin.\n",
"RESU\n"
" Resume plugin.\n",
NULL
};
@@ -1103,9 +1286,19 @@ cString cPluginSoftHdDevice::SVDRPCommand(const char *command,
__attribute__ ((unused)) int &reply_code)
{
if (!strcasecmp(command, "SUSP")) {
Suspend();
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
return "SoftHdDevice is suspended";
}
if (!strcasecmp(command, "RESU")) {
if (ShutdownHandler.GetUserInactiveTime()) {
ShutdownHandler.SetUserInactiveTimeout();
}
Resume();
cControl::Shutdown(); // not need, if not suspended
return "SoftHdDevice is resumed";
}
return NULL;
}

View File

@@ -21,7 +21,7 @@ SRC_URI=""
LICENSE="AGPL-3"
SLOT="0"
KEYWORDS="~x86 ~amd64"
IUSE="vaapi vdpau alsa oss yaepg opengl"
IUSE="vaapi vdpau alsa oss yaepg opengl jpeg"
DEPEND=">=x11-libs/libxcb-1.7
x11-libs/xcb-util
@@ -40,6 +40,7 @@ DEPEND=">=x11-libs/libxcb-1.7
vaapi? ( x11-libs/libva )
alsa? ( media-libs/alsa-lib )
oss? ( sys-kernel/linux-headers )
jpeg? ( virtual/jpeg )
"
src_prepare() {
@@ -49,11 +50,12 @@ src_prepare() {
src_compile() {
local myconf
myconf=""
myconf="-DHAVE_PTHREAD_NAME"
use vdpau && myconf="${myconf} -DUSE_VDPAU"
use vaapi && myconf="${myconf} -DUSE_VAAPI"
use alsa && myconf="${myconf} -DUSE_ALSA"
use oss && myconf="${myconf} -DUSE_OSS"
use jpeg && myconf="${myconf} -DUSE_JPEG"
emake all CC="$(tc-getCC)" CFLAGS="${CFLAGS}" \
LDFLAGS="${LDFLAGS}" CONFIG="${myconf}" LIBDIR="." || die

2022
video.c

File diff suppressed because it is too large Load Diff

View File

@@ -97,9 +97,15 @@ extern void VideoSetDenoise(int[]);
/// Set sharpen.
extern void VideoSetSharpen(int[]);
/// Set skip lines.
extern void VideoSetSkipLines(int);
/// Set audio delay.
extern void VideoSetAudioDelay(int);
/// Set auto-crop parameters.
extern void VideoSetAutoCrop(int, int, int);
/// Clear OSD.
extern void VideoOsdClear(void);
@@ -111,6 +117,9 @@ extern void VideoGetOsdSize(int *, int *);
extern int64_t VideoGetClock(void); ///< Get video clock.
/// Grab screen.
extern uint8_t *VideoGrab(int *, int *, int *, int);
extern void VideoOsdInit(void); ///< Setup osd.
extern void VideoOsdExit(void); ///< Cleanup osd.