43 Commits
0.4.5 ... 0.4.7

Author SHA1 Message Date
Johns
807b4df381 Release Version 0.4.7. 2012-02-13 23:21:11 +01:00
56edfd4f54 Fix bug: unscaled jpeg includes PNG header. 2012-02-13 20:15:25 +01:00
Johns
0a1a258d2a Update dependencies and install README. 2012-02-13 14:58:26 +01:00
Johns
09a0880d07 Update readme for new featurs. 2012-02-13 14:25:38 +01:00
Johns
a98a4adc7e Studio levels could be configured in setup menu. 2012-02-13 14:13:24 +01:00
Johns
f872f54e2a Window defaults to fullscreen without geometry. 2012-02-13 14:00:53 +01:00
33c638d538 Jpeg screengrab use VDR RgbToJpeg function. 2012-02-12 20:30:50 +01:00
Johns
0a2a221fa9 Add play/pause audio support. 2012-02-12 20:14:43 +01:00
Johns
24a065e5de Fix bug: audible glitch toggling AC-3 pass-through 2012-02-12 17:50:10 +01:00
Johns
6df970ca9e Fix bug: mpeg stills not displayed. 2012-02-12 16:57:32 +01:00
Johns
616cd9e133 Forgot to reenable FindAudioSync. 2012-02-11 18:29:20 +01:00
Johns
a91533f6d1 Detect audio stream type only after stream switch. 2012-02-11 18:22:48 +01:00
Johns
baa4500a2c Detect more h264 streams with leading zeros. 2012-02-11 17:18:44 +01:00
Johns
f28a737a9a VDPAU: support for studio levels added. 2012-02-10 15:47:52 +01:00
Johns
19cec561ba Fix bug #876: Keypad not working. 2012-02-10 10:43:31 +01:00
Johns
d8f63adaad Software deinterlacer (config/skip chroma deint):
Add support for skip chroma deinterlace to software deinterlacer.
Type of software deinterlacer now configurable from setup menu.
2012-02-09 21:22:42 +01:00
Johns
8c16466d31 Set mixer channel through command line option 2012-02-09 16:01:36 +01:00
Johns
ced54a5cf1 Fix bug: LFE moved to wrong position. 2012-02-09 00:46:02 +01:00
Johns
08246b5ac3 Guard suspend/resume against multiple calls. 2012-02-08 23:26:49 +01:00
Johns
c3a1de8c7b jpeg_mem_dest only supported by jpeg 8.0. 2012-02-08 22:32:47 +01:00
Johns
918170d00b Add support for AAC LATM audio streams. 2012-02-08 15:19:18 +01:00
Johns
bc50f37c4d Spatial deinterlacer for VA-API. 2012-02-07 18:18:13 +01:00
Johns
09cf1f5c85 Fix bug: alsa+ffmpeg use different channel layout. 2012-02-07 17:08:59 +01:00
Johns
947f6b312e Support more LPCM sample rates and channels. 2012-02-06 23:54:22 +01:00
Johns
99728258f1 Quick&dirty support for mpeg LPCM streams. 2012-02-06 22:58:42 +01:00
Johns
c972f8c4dd Workaround for text2skin undrawn OSD areas. 2012-02-06 20:54:20 +01:00
Johns
7d38dff5bf Detect dvb LPCM stream and ignore it. 2012-02-05 14:17:46 +01:00
Johns
8db8b68edd Makes Workarounds command line configurable. 2012-02-04 16:38:10 +01:00
Johns
00cafd18ed Release Version 0.4.6. 2012-02-02 23:32:51 +01:00
Johns
ab4e89132e Auto-crop improvement and nicer a-v sync display. 2012-02-02 16:01:38 +01:00
Johns
3585f1df19 Increase audio buffer, if bigger audio delay used. 2012-02-02 16:01:08 +01:00
Johns
e258c35537 Makes SkipLines configure in setup menu. 2012-02-02 16:00:26 +01:00
Johns
91dbe46786 Add A-V info output and compile time option. 2012-02-01 23:12:45 +01:00
Johns
a7389111ff Fix bug: VA-API intel software decoder broken. 2012-02-01 18:36:24 +01:00
Johns
27e9a88e2f Video updates and bug fix.
Check if surface is ready in VaapiGetSurface.
Set PTS/DTS only in the first split video packet.
Add support for 4:3 output modes.
Quicker auto-crop after channel switch.
Add auto-crop support for Intel VA-API backend.
Fix bug: Auto-Crop logo skip didn't use displayed width.
2012-02-01 16:50:48 +01:00
Johns
33e9c71aea Removed debug printf. 2012-01-31 20:48:47 +01:00
Johns
bd84e3f3b9 Workaround for mpeg2 FFMpeg+VA-API+Intel GPU hung. 2012-01-31 20:45:09 +01:00
Johns
364cc04736 Fix bug: Only black picture with VA-API hw decoder. 2012-01-30 23:09:53 +01:00
ec4a899bb8 Add support to start the plugin in suspended mode. 2012-01-30 17:03:15 +01:00
Johns
dab31e2367 Finished rewrite of video code, to support modules. 2012-01-30 15:58:21 +01:00
Johns
e613ff1f7e Add aspect change support to software decoder path 2012-01-29 23:57:22 +01:00
Johns
1886b745e5 Repair software decoder with vaapi vdpau backend. 2012-01-29 19:28:46 +01:00
Johns
422c378a5e Add workaround for Intel VA-API MPEG GPU hung. 2012-01-29 11:28:10 +01:00
12 changed files with 2411 additions and 1006 deletions

View File

@@ -1,5 +1,79 @@
User johns
Date:
Date: Mon Feb 13 23:20:26 CET 2012
Release Version 0.4.7
User FireFly
Date: Mon Feb 13 20:14:11 CET 2012
Fix bug: unscaled jpeg includes PNG header.
User johns
Date: Mon Feb 13 14:58:26 CET 2012
VDPAU: Studio levels could be configured in the setup menu.
Window size defaults to fullscreen, if no geometry is given.
User m.Rcu
Date: Sun Feb 12 20:28:22 CET 2012
Jpeg screengrab use VDR RgbToJpeg function.
User johns
Date: Sun Feb 12 20:14:43 CET 2012
Add play/pause audio support.
Fix bug: audible glitch when switching AC-3 pass-through <-> none.
Fix bug: mpeg stills not displayed.
Detect audio stream type only after stream switch.
Detect more h264 streams with leading zeros.
VDPAU: support for studio levels added.
Add support for skip chroma deinterlace to software deinterlacer.
Type of software deinterlacer now configurable from setup menu.
Mixer channel could be set through command line option.
Fix bug: LFE moved to wrong position.
Guard suspend/resume against multiple calls.
Add support for AAC LATM audio streams.
Fix bug: alsa and ffmpeg use different channel layout.
Support more LPCM sample rates and number of channels.
Quick&dirty support for mpeg LPCM streams.
Workaround for text2skin undrawn OSD areas.
Detect dvb LPCM stream and ignore it.
User johns
Date: Thu Feb 2 23:29:35 CET 2012
Release Version 0.4.6
Warn only on the first duplicated frame in sequence.
Increase audio buffer, if bigger audio delay is used.
Makes SkipLines configure in setup menu.
Auto-crop only enabled with normal 4:3 display mode.
Vaapi updates OSD when cropping changes.
Add A-V info output and compile time option.
Fix bug: VA-API intel software decoder broken by aspect commit.
Add support for 4:3 output modes.
Quicker auto-crop after channel switch.
Add auto-crop support for Intel VA-API backend.
Fix bug: Auto-Crop logo skip didn't use displayed width.
Workaround for mpeg2 FFMpeg + VA-API + Intel GPU hung.
Fix bug: Missing vaSyncSurface and vaDestroyImage.
Fix bug: Only black picture with VA-API hw decoder.
User HelAu
Date: Mon Jan 30 16:54:47 CET 2012
Add support to start the plugin in suspended mode.
User johns
Date: Mon Jan 30 15:58:21 CET 2012
Finished rewrite of video code, to support output modules.
Add aspect change support to software decoder path.
Repair software decoder with vaapi vdpau backend.
Add workaround for Intel VA-API MPEG GPU hung.
User johns
Date: Sat Jan 28 13:32:12 CET 2012
Release Version 0.4.5
Add configurable skip lines at video top and bottom.

View File

@@ -19,11 +19,11 @@ GIT_REV = $(shell git describe --always 2>/dev/null)
### Configuration (edit this for your needs)
CONFIG := #-DDEBUG
CONFIG += -DAV_INFO
#CONFIG += -DHAVE_PTHREAD_NAME
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:
@@ -31,7 +31,8 @@ CONFIG += -DUSE_OSS
CC ?= gcc
CXX ?= g++
CFLAGS ?= -g -O2 -W -Wall -Wextra -Winit-self \
-Wdeclaration-after-statement
-Wdeclaration-after-statement \
-ftree-vectorize -msse3 -flax-vector-conversions
CXXFLAGS ?= -g -O2 -W -Wall -Wextra -Woverloaded-virtual
### The directory environment:
@@ -90,9 +91,7 @@ LIBS += -lrt \
$(if $(findstring USE_VAAPI,$(CONFIG)), \
`pkg-config --libs libva-x11 libva-glx libva`) \
$(if $(findstring USE_ALSA,$(CONFIG)), \
`pkg-config --libs alsa`) \
$(if $(findstring USE_JPEG,$(CONFIG)), \
-ljpeg)
`pkg-config --libs alsa`)
### The object files (add further files here):

View File

@@ -76,6 +76,13 @@ Setup: environment
DISPLAY=:0.0
x11 display name
NO_HW=1
if set don't use the hardware decoders
NO_MPEG_HW=1
if set don't use the hardware decoder for mpeg1/2
STUDIO_LEVELS=1
if set use studio levels with vdpau (deprecated use setup)
only if alsa is configured
ALSA_DEVICE=default
alsa PCM device name
@@ -85,6 +92,7 @@ Setup: environment
alsa control device name
ALSA_MIXER_CHANNEL=PCM
alsa control channel name
only if oss is configured
OSS_AUDIODEV=/dev/dsp
oss dsp device name
@@ -105,7 +113,7 @@ Setup: /etc/vdr/setup.conf
softhddevice.HideMainMenuEntry = 0
0 = show softhddevice main menu entry, 1 = hide entry
<res> of the next parameters is 567i, 720p, 1080i_fake or 1080i.
<res> of the next parameters is 576i, 720p, 1080i_fake or 1080i.
1080i_fake is 1280x1080 or 1440x1080
1080i is "real" 1920x1080
@@ -146,6 +154,12 @@ Setup: /etc/vdr/setup.conf
if detected crop area is too small, cut max 'n' pixels at top and
bottom.
softhddevice.SkipLines = 0
skip 'n' lines at top and bottom of the video picture.
softhddevice.StudioLevels = 0
0 use PC levels (0-255) with vdpau.
1 use studio levels (16-235) with vdpau.
softhddevice.Suspend.Close = 0
1 suspend closes x11 window, connection and audio device.
@@ -154,6 +168,11 @@ Setup: /etc/vdr/setup.conf
softhddevice.Suspend.X11 = 0
1 suspend stops X11 server (not working yet)
VideoDisplayFormat = ?
0 pan and scan
1 letter box
2 center cut-out
Setup: /etc/vdr/remote.conf
------

37
Todo
View File

@@ -21,11 +21,11 @@ $Id: $
missing:
software deinterlace (yadif, ...)
software decoder with software deinterlace
zoom/fit-zoom 4:3 (SetVideoDisplayFormat, SetVideoFormat?)
ITU BT601, ITU BT709 (HD), RGB studio levels (16-235)?
suspend output / energie saver: stop and restart X11
suspend plugin didn't restore full-screen (is this wanted?)
Option deinterlace off / deinterlace force!
Make output drivers better modular (under construction).
ColorSpace aren't configurable with the gui.
Inverse telecine isn't configurable with the gui.
crash:
AudioPlayHandlerThread -> pthread_cond_wait
@@ -35,18 +35,20 @@ video:
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.
OSD can only be shown after some stream could be shown
yaepghd changed position is lost on channel switch
pause (live tv) has sometime problems with SAT1 HD Pro7 HD
vdpau:
software decoder path not working
libva:
yaepghd (VaapiSetOutputPosition) support
can associate only displayed part of osd
grab image for va-api
still many:
remove stderr output of libva init
still many: (workaround export NO_MPEG_HW=1)
[drm:i915_hangcheck_elapsed] *ERROR* Hangcheck timer elapsed... GPU hung
[drm:i915_wait_request] *ERROR* i915_wait_request returns -11 ...
@@ -54,28 +56,33 @@ libva: branch vaapi-ext
add support for vaapi-ext
libva-intel-driver:
deinterlace only supported with vaapi-ext
1080i does no v-sync (sometimes correct working with vaapi-ext)
OSD has sometimes wrong size (workaround written)
software decoder needs UV swab
sometimes software decoder deinterlace isn't working and 1080i channels
show artefacts
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
OSD still has some problems with auto-crop and 4:3 zoom.
libva-xvba-driver:
with auto-crop OSD has wrong position
x11:
disable screensaver
skip multiple configure-notify, handle only the last one.
support embedded mode
audio:
write TS -> PES parser, which feeds audio before the next start packet
Combine alsa+oss ringbuffer code.
Make alsa thread/polled and oss thread/polled output module runtime
selectable.
software volume support
software volume support (could be done with asound.conf)
Mute should do a real mute and not only set volume to zero.
Starting suspended and muted, didn't register the mute.
audio/alsa:
better downmix of >2 channels on 2 channel hardware
@@ -85,12 +92,11 @@ audio/alsa:
audio/oss:
alsa oss emulation mixer "pcm" not working
oss4 mixer channel not working
ring buffer overflow with alsa oss emulation
HDMI/SPDIF Passthrough:
only AC-3 written
Channels are wrong setup, if changing setting during operation.
support oss pass-through
playback of recording
pause is not reset, when replay exit
@@ -105,8 +111,6 @@ setup:
Can a notice be added to the setup menu?
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)
@@ -118,5 +122,6 @@ future features (not planed for 1.0 - 1.5)
atmolight support
multistream handling
pip support
save and use auto-crop with channel zapping
upmix stereo to AC-3
upmix stereo to AC-3 (supported by alsa plugin)

156
audio.c
View File

@@ -113,6 +113,8 @@ typedef struct _audio_module_
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
} AudioModule;
@@ -123,6 +125,8 @@ static const AudioModule NoopModule; ///< forward definition of noop module
// Variables
//----------------------------------------------------------------------------
char AudioAlsaDriverBroken; ///< disable broken driver message
static const char *AudioModuleName; ///< which audio module to use
/// Selected audio module.
@@ -132,12 +136,12 @@ 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
static int AudioPaused; ///< audio paused
static volatile char AudioPaused; ///< audio paused
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
static const int AudioBufferTime = 350; ///< audio buffer time in ms
#ifdef USE_AUDIO_THREAD
static pthread_t AudioThread; ///< audio play thread
@@ -147,6 +151,8 @@ static pthread_cond_t AudioStartCond; ///< condition variable
static const int AudioThread; ///< dummy audio thread
#endif
extern int VideoAudioDelay; /// import audio/video delay
#ifdef USE_AUDIORING
//----------------------------------------------------------------------------
@@ -332,7 +338,9 @@ static int AlsaPlayRingbuffer(void)
if (first) {
// happens with broken alsa drivers
if (AudioThread) {
if (!AudioAlsaDriverBroken) {
Error(_("audio/alsa: broken driver %d\n"), avail);
}
usleep(5 * 1000);
}
}
@@ -561,7 +569,6 @@ static void AlsaEnqueue(const void *samples, int count)
state = snd_pcm_state(AlsaPCMHandle);
Debug(3, "audio/alsa: state %s\n", snd_pcm_state_name(state));
Debug(3, "audio/alsa: unpaused\n");
AudioPaused = 0;
}
}
// Update audio clock
@@ -620,6 +627,9 @@ static void AlsaThread(void)
AlsaFlushBuffer = 0;
break;
}
if (AudioPaused) {
break;
}
// wait for space in kernel buffers
if ((err = snd_pcm_wait(AlsaPCMHandle, 100)) < 0) {
Error(_("audio/alsa: wait underrun error?\n"));
@@ -631,7 +641,7 @@ static void AlsaThread(void)
usleep(100 * 1000);
continue;
}
if (AlsaFlushBuffer) {
if (AlsaFlushBuffer || AudioPaused) {
continue;
}
if ((err = AlsaPlayRingbuffer())) { // empty / error
@@ -816,7 +826,7 @@ static void AlsaInitMixer(void)
const char *name;
name = snd_mixer_selem_get_name(alsa_mixer_elem);
if (strcasecmp(name, alsa_mixer_elem_name) == 0) {
if (!strcasecmp(name, alsa_mixer_elem_name)) {
snd_mixer_selem_get_playback_volume_range(alsa_mixer_elem,
&alsa_mixer_elem_min, &alsa_mixer_elem_max);
AlsaRatio =
@@ -900,6 +910,7 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
snd_pcm_uframes_t period_size;
int err;
int ret;
int delay;
snd_pcm_t *handle;
if (!AlsaPCMHandle) { // alsa not running yet
@@ -1085,11 +1096,14 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
AlsaStartThreshold = snd_pcm_frames_to_bytes(AlsaPCMHandle, period_size);
// buffer time/delay in ms
delay = AudioBufferTime;
if (VideoAudioDelay > -100) {
delay += 100 + VideoAudioDelay / 90;
}
if (AlsaStartThreshold <
(*freq * *channels * AudioBytesProSample * AudioBufferTime) / 1000U) {
(*freq * *channels * AudioBytesProSample * delay) / 1000U) {
AlsaStartThreshold =
(*freq * *channels * AudioBytesProSample * AudioBufferTime) /
1000U;
(*freq * *channels * AudioBytesProSample * delay) / 1000U;
}
// no bigger, than the buffer
if (AlsaStartThreshold > RingBufferFreeBytes(AlsaRingBuffer)) {
@@ -1101,6 +1115,47 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
return ret;
}
/**
** Play audio.
*/
void AlsaPlay(void)
{
int err;
if (AlsaCanPause) {
if ((err = snd_pcm_pause(AlsaPCMHandle, 0))) {
Error(_("audio/alsa: snd_pcm_pause(): %s\n"), snd_strerror(err));
}
} else {
if ((err = snd_pcm_prepare(AlsaPCMHandle)) < 0) {
Error(_("audio/alsa: snd_pcm_prepare(): %s\n"), snd_strerror(err));
}
}
#ifdef DEBUG
if (snd_pcm_state(AlsaPCMHandle) == SND_PCM_STATE_PAUSED) {
Error(_("audio/alsa: still paused\n"));
}
#endif
}
/**
** Pause audio.
*/
void AlsaPause(void)
{
int err;
if (AlsaCanPause) {
if ((err = snd_pcm_pause(AlsaPCMHandle, 1))) {
Error(_("snd_pcm_pause(): %s\n"), snd_strerror(err));
}
} else {
if ((err = snd_pcm_drop(AlsaPCMHandle)) < 0) {
Error(_("snd_pcm_drop(): %s\n"), snd_strerror(err));
}
}
}
/**
** Empty log callback
*/
@@ -1169,6 +1224,8 @@ static const AudioModule AlsaModule = {
.GetDelay = AlsaGetDelay,
.SetVolume = AlsaSetVolume,
.Setup = AlsaSetup,
.Play = AlsaPlay,
.Pause = AlsaPause,
.Init = AlsaInit,
.Exit = AlsaExit,
};
@@ -1378,6 +1435,9 @@ static void OssThread(void)
OssFlushBuffer = 0;
break;
}
if (AudioPaused) {
break;
}
fds[0].fd = OssPcmFildes;
fds[0].events = POLLOUT | POLLERR;
@@ -1389,7 +1449,7 @@ static void OssThread(void)
continue;
}
if (OssFlushBuffer) {
if (OssFlushBuffer || AudioPaused) {
continue;
}
@@ -1622,6 +1682,7 @@ static int OssSetup(int *freq, int *channels, int use_ac3)
{
int ret;
int tmp;
int delay;
if (OssPcmFildes == -1) { // OSS not ready
return -1;
@@ -1708,12 +1769,14 @@ static int OssSetup(int *freq, int *channels, int use_ac3)
// start when enough bytes for initial write
OssStartThreshold = bi.bytes + tmp;
// buffer time/delay in ms
delay = AudioBufferTime;
if (VideoAudioDelay > -100) {
delay += 100 + VideoAudioDelay / 90;
}
if (OssStartThreshold <
(*freq * *channels * AudioBytesProSample * AudioBufferTime) /
1000U) {
(*freq * *channels * AudioBytesProSample * delay) / 1000U) {
OssStartThreshold =
(*freq * *channels * AudioBytesProSample * AudioBufferTime) /
1000U;
(*freq * *channels * AudioBytesProSample * delay) / 1000U;
}
// no bigger, than the buffer
if (OssStartThreshold > RingBufferFreeBytes(OssRingBuffer)) {
@@ -1727,6 +1790,20 @@ static int OssSetup(int *freq, int *channels, int use_ac3)
return ret;
}
/**
** Play audio.
*/
void OssPlay(void)
{
}
/**
** Pause audio.
*/
void OssPause(void)
{
}
/**
** Initialize OSS audio output module.
*/
@@ -1772,6 +1849,8 @@ static const AudioModule OssModule = {
.GetDelay = OssGetDelay,
.SetVolume = OssSetVolume,
.Setup = OssSetup,
.Play = OssPlay,
.Pause = OssPause,
.Init = OssInit,
.Exit = OssExit,
};
@@ -1855,6 +1934,8 @@ static const AudioModule NoopModule = {
.GetDelay = NoopGetDelay,
.SetVolume = NoopSetVolume,
.Setup = NoopSetup,
.Play = NoopVoid,
.Pause = NoopVoid,
.Init = NoopVoid,
.Exit = NoopVoid,
};
@@ -2057,13 +2138,7 @@ int64_t AudioGetClock(void)
*/
void AudioSetVolume(int volume)
{
#ifdef USE_ALSA
AlsaSetVolume(volume);
#endif
#ifdef USE_OSS
OssSetVolume(volume);
#endif
(void)volume;
return AudioUsedModule->SetVolume(volume);
}
/**
@@ -2097,6 +2172,32 @@ int AudioSetup(int *freq, int *channels, int use_ac3)
return AudioUsedModule->Setup(freq, channels, use_ac3);
}
/**
** Play audio.
*/
void AudioPlay(void)
{
if (!AudioPaused) {
Warning("audio: not paused, check the code\n");
return;
}
Debug(3, "audio: resumed\n");
AudioPaused = 0;
}
/**
** Pause audio.
*/
void AudioPause(void)
{
if (AudioPaused) {
Warning("audio: already paused, check the code\n");
return;
}
Debug(3, "audio: paused\n");
AudioPaused = 1;
}
/**
** Set pcm audio device.
**
@@ -2137,6 +2238,18 @@ void AudioSetDeviceAC3(const char *device)
AudioAC3Device = device;
}
/**
** Set pcm audio mixer channel.
**
** @param channel name of the mixer channel (fe. PCM or Master)
**
** @note this is currently used to select alsa/OSS output module.
*/
void AudioSetChannel(const char *channel)
{
AudioMixerChannel = channel;
}
/**
** Initialize audio output module.
**
@@ -2188,8 +2301,7 @@ void AudioInit(void)
AudioInitThread();
}
#endif
AudioPaused = 1;
AudioPaused = 0;
}
/**

13
audio.h
View File

@@ -39,12 +39,19 @@ extern int64_t AudioGetClock(); ///< get current audio clock
extern void AudioSetVolume(int); ///< set volume
extern int AudioSetup(int *, int *, int); ///< setup audio output
//extern void AudioPlay(void); ///< play audio
//extern void AudioPause(void); ///< pause audio
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 AudioSetDeviceAC3(const char *); ///< set pass-through device
extern void AudioSetChannel(const char *); ///< set mixer channel
extern void AudioInit(void); ///< setup audio module
extern void AudioExit(void); ///< cleanup and exit audio module
//----------------------------------------------------------------------------
// Variables
//----------------------------------------------------------------------------
extern char AudioAlsaDriverBroken; ///< disable broken driver message
/// @}

80
codec.c
View File

@@ -605,6 +605,7 @@ struct _audio_decoder_
/// audio parser to support insane dvb streaks
AVCodecParserContext *AudioParser;
int PassthroughAC3; ///< current ac-3 pass-through
int SampleRate; ///< current stream sample rate
int Channels; ///< current stream channels
@@ -621,6 +622,9 @@ static char CodecPassthroughAC3; ///< pass ac3 through
//static char CodecPassthroughDTS; ///< pass dts through (unsupported)
//static char CodecPassthroughMPA; ///< pass mpa through (unsupported)
#else
static const int CodecPassthroughAC3 = 0;
#endif
/**
@@ -735,10 +739,65 @@ void CodecSetAudioPassthrough(int mask)
#ifdef USE_PASSTHROUGH
CodecPassthroughAC3 = mask & 1 ? 1 : 0;
#endif
// FIXME: must update audio decoder (nr. of channels wrong)
(void)mask;
}
/**
** Reorder audio frame.
**
** 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
*/
static void CodecReorderAudioFrame(int16_t * buf, int size, int channels)
{
int i;
int c;
int ls;
int rs;
int lfe;
switch (channels) {
case 5:
size /= 2;
for (i = 0; i < size; i += 5) {
c = buf[i + 2];
ls = buf[i + 3];
rs = buf[i + 4];
buf[i + 2] = ls;
buf[i + 3] = rs;
buf[i + 4] = c;
}
break;
case 6:
size /= 2;
for (i = 0; i < size; i += 6) {
c = buf[i + 2];
lfe = buf[i + 3];
ls = buf[i + 4];
rs = buf[i + 5];
buf[i + 2] = ls;
buf[i + 3] = rs;
buf[i + 4] = c;
buf[i + 5] = lfe;
}
break;
case 8:
size /= 2;
for (i = 0; i < size; i += 8) {
c = buf[i + 2];
lfe = buf[i + 3];
ls = buf[i + 4];
rs = buf[i + 5];
buf[i + 2] = ls;
buf[i + 3] = rs;
buf[i + 4] = c;
buf[i + 5] = lfe;
}
break;
}
}
#ifdef USE_AVPARSER
/**
@@ -796,6 +855,10 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
dpkt->dts = audio_decoder->AudioParser->dts;
buf_sz = sizeof(buf);
l = avcodec_decode_audio3(audio_ctx, buf, &buf_sz, dpkt);
if (l == AVERROR(EAGAIN)) {
index += n; // this is needed for aac latm
continue;
}
if (l < 0) { // no audio frame could be decompressed
Error(_("codec: error audio data at %d\n"), index);
break;
@@ -812,11 +875,15 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
AudioSetClock(dpkt->pts);
}
// FIXME: must first play remainings bytes, than change and play new.
if (audio_decoder->SampleRate != audio_ctx->sample_rate
if (audio_decoder->PassthroughAC3 != CodecPassthroughAC3
|| audio_decoder->SampleRate != audio_ctx->sample_rate
|| audio_decoder->Channels != audio_ctx->channels) {
int err;
int isAC3;
audio_decoder->PassthroughAC3 = CodecPassthroughAC3;
// FIXME: use swr_convert from swresample (only in ffmpeg!)
// FIXME: tell ac3 decoder to use downmix
if (audio_decoder->ReSample) {
audio_resample_close(audio_decoder->ReSample);
audio_decoder->ReSample = NULL;
@@ -825,14 +892,11 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
audio_decoder->SampleRate = audio_ctx->sample_rate;
audio_decoder->HwSampleRate = audio_ctx->sample_rate;
audio_decoder->Channels = audio_ctx->channels;
#ifdef USE_PASSTHROUGH
// SPDIF/HDMI passthrough
if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
audio_decoder->HwChannels = 2;
isAC3 = 1;
} else
#endif
{
} else {
audio_decoder->HwChannels = audio_ctx->channels;
isAC3 = 0;
}
@@ -893,6 +957,8 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
audio_decoder->HwChannels *
av_get_bytes_per_sample(audio_ctx->sample_fmt);
Debug(4, "codec/audio: %d -> %d\n", buf_sz, outlen);
CodecReorderAudioFrame(outbuf, outlen,
audio_decoder->HwChannels);
AudioEnqueue(outbuf, outlen);
}
} else {
@@ -971,6 +1037,8 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
// True HD?
#endif
#endif
CodecReorderAudioFrame(buf, buf_sz,
audio_decoder->HwChannels);
AudioEnqueue(buf, buf_sz);
}
}

View File

@@ -39,9 +39,6 @@
#define __USE_GNU
#endif
#include <pthread.h>
#ifdef USE_JPEG
#include <jpeglib.h>
#endif
#include "misc.h"
#include "softhddev.h"
@@ -61,10 +58,12 @@ static char ConfigVdpauDecoder = 1; ///< use vdpau decoder, if possible
#endif
static char ConfigFullscreen; ///< fullscreen modus
static char ConfigStartSuspended; ///< flag to start in suspend mode
static char ConfigStartX11Server; ///< flag start the x11 server
static pthread_mutex_t SuspendLockMutex; ///< suspend lock mutex
static volatile char VideoFreezed; ///< video freezed
static volatile char StreamFreezed; ///< stream freezed
//////////////////////////////////////////////////////////////////////////////
// Audio
@@ -74,6 +73,7 @@ static volatile char NewAudioStream; ///< new audio stream
static volatile char SkipAudio; ///< skip audio stream
static AudioDecoder *MyAudioDecoder; ///< audio decoder
static enum CodecID AudioCodecID; ///< current codec id
static int AudioChannelID; ///< current audio channel id
/**
** mpeg bitrate table.
@@ -200,8 +200,7 @@ static int FindAudioSync(const AVPacket * avpkt)
** @param size size of PES packet
** @param id PES packet type
*/
int PlayAudio(const uint8_t * data, int size,
__attribute__ ((unused)) uint8_t id)
int PlayAudio(const uint8_t * data, int size, uint8_t id)
{
int n;
int osize;
@@ -209,7 +208,7 @@ int PlayAudio(const uint8_t * data, int size,
// channel switch: SetAudioChannelDevice: SetDigitalAudioDevice:
if (VideoFreezed) { // video freezed
if (StreamFreezed) { // stream freezed
return 0;
}
if (SkipAudio || !MyAudioDecoder) { // skip audio
@@ -259,8 +258,11 @@ int PlayAudio(const uint8_t * data, int size,
return osize;
}
// Detect audio code
// MPEG-PS mp2 MPEG1, MPEG2, AC3
// MPEG-PS mp2 MPEG1, MPEG2, AC3, LPCM, AAC LATM
// only if unknown or new channel id
if (AudioCodecID == CODEC_ID_NONE || AudioChannelID != id) {
AudioChannelID = id;
// Syncword - 0x0B77
if (data[0] == 0x0B && data[1] == 0x77) {
if (AudioCodecID != CODEC_ID_AC3) {
@@ -277,6 +279,51 @@ int PlayAudio(const uint8_t * data, int size,
CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_MP2);
AudioCodecID = CODEC_ID_MP2;
}
// latm header 0x56E0 11bits: 0x2B7
} else if (data[0] == 0x56 && (data[1] & 0xE0) == 0xE0) {
// && (((data[1] & 0x1F) << 8) + (data[2] & 0xFF)) < size
if (AudioCodecID != CODEC_ID_AAC_LATM) {
Debug(3, "[softhddev]%s: AAC LATM %d\n", __FUNCTION__, id);
CodecAudioClose(MyAudioDecoder);
CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_AAC_LATM);
AudioCodecID = CODEC_ID_AAC_LATM;
}
// Private stream + LPCM ID
} else if (data[-n - 9 + 3] == 0xBD && data[0] == 0xA0) {
if (AudioCodecID != CODEC_ID_PCM_DVD) {
static int samplerates[] = { 48000, 96000, 44100, 32000 };
int samplerate;
int channels;
int bits_per_sample;
Debug(3, "[softhddev]%s: LPCM %d sr:%d bits:%d chan:%d\n",
__FUNCTION__, id, data[5] >> 4,
(((data[5] >> 6) & 0x3) + 4) * 4, (data[5] & 0x7) + 1);
CodecAudioClose(MyAudioDecoder);
bits_per_sample = (((data[5] >> 6) & 0x3) + 4) * 4;
if (bits_per_sample != 16) {
Error(_
("softhddev: LPCM %d bits per sample aren't supported\n"),
bits_per_sample);
// FIXME: handle unsupported formats.
}
samplerate = samplerates[data[5] >> 4];
channels = (data[5] & 0x7) + 1;
AudioSetup(&samplerate, &channels, 0);
if (samplerate != samplerates[data[5] >> 4]) {
Error(_("softhddev: LPCM %d sample-rate is unsupported\n"),
samplerates[data[5] >> 4]);
// FIXME: support resample
}
if (channels != (data[5] & 0x7) + 1) {
Error(_("softhddev: LPCM %d channels are unsupported\n"),
(data[5] & 0x7) + 1);
// FIXME: support resample
}
//CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_PCM_DVD);
AudioCodecID = CODEC_ID_PCM_DVD;
}
} else {
// no start package
// FIXME: Nick/Viva sends this shit, need to find sync in packet
@@ -297,26 +344,41 @@ int PlayAudio(const uint8_t * data, int size,
data += n;
size -= n;
}
// no decoder or codec known
}
// still no decoder or codec known
if (AudioCodecID == CODEC_ID_NONE) {
return osize;
}
}
if (AudioCodecID == CODEC_ID_PCM_DVD) {
if (size > 7) {
char *buf;
if (!(buf = malloc(size - 7))) {
Error(_("softhddev: out of memory\n"));
} else {
swab(data + 7, buf, size - 7);
AudioEnqueue(buf, size - 7);
free(buf);
}
}
} else {
avpkt->data = (void *)data;
avpkt->size = size;
//memset(avpkt->data + avpkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
CodecAudioDecode(MyAudioDecoder, avpkt);
}
return osize;
}
/**
** Mute audio device.
** Turns off audio while replaying.
*/
void Mute(void)
{
SkipAudio = 1;
AudioFlushBuffers();
//AudioSetVolume(0);
}
@@ -421,6 +483,7 @@ static void VideoEnqueue(int64_t pts, const void *data, int size)
// new + grow reserves FF_INPUT_BUFFER_PADDING_SIZE
av_grow_packet(avpkt, ((size + VIDEO_BUFFER_SIZE / 2)
/ (VIDEO_BUFFER_SIZE / 2)) * (VIDEO_BUFFER_SIZE / 2));
// FIXME: out of memory!
#ifdef DEBUG
if (avpkt->size <= avpkt->stream_index + size) {
fprintf(stderr, "%d %d %d\n", avpkt->size, avpkt->stream_index,
@@ -485,6 +548,50 @@ static void VideoNextPacket(int codec_id)
avpkt->dts = AV_NOPTS_VALUE;
}
/**
** Fix packet for FFMpeg.
**
** Some tv-stations sends mulitple pictures in a singe PES packet.
** Current ffmpeg 0.10 and libav-0.8 has problems with this.
** Split the packet into single picture packets.
*/
void FixPacketForFFMpeg(VideoDecoder * MyVideoDecoder, AVPacket * avpkt)
{
uint8_t *p;
int n;
AVPacket tmp[1];
int first;
p = avpkt->data;
n = avpkt->size;
*tmp = *avpkt;
first = 1;
while (n > 4) {
// scan for picture header 0x00000100
if (!p[0] && !p[1] && p[2] == 0x01 && !p[3]) {
if (first) {
first = 0;
n -= 4;
p += 4;
continue;
}
// packet has already an picture header
tmp->size = p - tmp->data;
CodecVideoDecode(MyVideoDecoder, tmp);
// time-stamp only valid for first packet
tmp->pts = AV_NOPTS_VALUE;
tmp->dts = AV_NOPTS_VALUE;
tmp->data = p;
tmp->size = n;
}
--n;
++p;
}
CodecVideoDecode(MyVideoDecoder, tmp);
}
/**
** Decode from PES packet ringbuffer.
*/
@@ -495,7 +602,7 @@ int VideoDecode(void)
int saved_size;
static int last_codec_id = CODEC_ID_NONE;
if (VideoFreezed) {
if (StreamFreezed) { // stream freezed
return 1;
}
if (VideoClearBuffers) {
@@ -559,7 +666,33 @@ int VideoDecode(void)
avpkt->size = avpkt->stream_index;
avpkt->stream_index = 0;
if (0) {
static int done;
if (done < 2) {
int fildes;
int who_designed_this_is____;
if (done == 0)
fildes =
open("frame0.pes", O_WRONLY | O_TRUNC | O_CREAT, 0666);
else if (done == 1)
fildes =
open("frame1.pes", O_WRONLY | O_TRUNC | O_CREAT, 0666);
else
fildes =
open("frame2.pes", O_WRONLY | O_TRUNC | O_CREAT, 0666);
done++;
who_designed_this_is____ = write(fildes, avpkt->data, avpkt->size);
close(fildes);
}
}
if (last_codec_id == CODEC_ID_MPEG2VIDEO) {
FixPacketForFFMpeg(MyVideoDecoder, avpkt);
} else {
CodecVideoDecode(MyVideoDecoder, avpkt);
}
avpkt->size = saved_size;
@@ -674,6 +807,8 @@ int PlayVideo(const uint8_t * data, int size)
const uint8_t *check;
int64_t pts;
int n;
int z;
int l;
if (Usr1Signal) { // x11 server ready
Usr1Signal = 0;
@@ -685,7 +820,7 @@ int PlayVideo(const uint8_t * data, int size)
if (SkipVideo) { // skip video
return size;
}
if (VideoFreezed) { // video freezed
if (StreamFreezed) { // stream freezed
return 0;
}
if (NewVideoStream) { // channel switched
@@ -748,9 +883,72 @@ int PlayVideo(const uint8_t * data, int size)
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]);
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;
while (!*check) { // count leading zeros
if (--l < 4) {
Error(_("[softhddev] invalid video packet %d bytes\n"), size);
return size;
}
++check;
++z;
}
// H264 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) {
VideoNextPacket(CODEC_ID_H264);
} else {
Debug(3, "video: h264 detected\n");
VideoCodecID = CODEC_ID_H264;
}
// SKIP PES header
VideoEnqueue(pts, check - 5, l + 5);
return size;
}
// PES start code 0x00 0x00 0x01
if (z > 1 && check[0] == 0x01) {
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
// SKIP PES header
VideoEnqueue(pts, check - 3, l + 3);
return size;
}
// this happens when vdr sends incomplete packets
if (VideoCodecID == CODEC_ID_NONE) {
Debug(3, "video: not detected\n");
return size;
}
// SKIP PES header
VideoEnqueue(pts, data + 9 + n, size - 9 - n);
// 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
VideoNextPacket(CODEC_ID_MPEG2VIDEO);
return size;
}
return size;
#else
// FIXME: no valid mpeg2/h264 detection yet
// FIXME: better skip all zero's >3 && 0x01 0x09 h264, >2 && 0x01 -> mpeg2
@@ -809,10 +1007,26 @@ int PlayVideo(const uint8_t * data, int size)
VideoEnqueue(pts, check, size - 9 - n);
return size;
#endif
}
#ifdef USE_JPEG
/// call VDR support function
extern uint8_t *CreateJpeg(uint8_t *, int *, int, int, int);
#if defined(USE_JPEG) && JPEG_LIB_VERSION >= 80
/**
** Create a jpeg image in memory.
**
** @param image raw RGB image
** @param raw_size size of raw image
** @param size[out] size of jpeg image
** @param quality jpeg quality
** @param width number of horizontal pixels in image
** @param height number of vertical pixels in image
**
** @returns allocated jpeg image.
*/
uint8_t *CreateJpeg(uint8_t * image, int raw_size, int *size, int quality,
int width, int height)
{
@@ -865,21 +1079,15 @@ uint8_t *CreateJpeg(uint8_t * image, int raw_size, int *size, int quality,
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;
uint8_t *image;
int raw_size = 0;
raw_size = 0;
image = VideoGrab(&raw_size, &width, &height, 0);
jpg_image = CreateJpeg(image, raw_size, size, quality, width, height);
jpg_image = CreateJpeg(image, 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"));
@@ -894,6 +1102,10 @@ uint8_t *GrabImage(int *size, int jpeg, int quality, int width, int height)
*/
void SetPlayMode(void)
{
if (ConfigStartSuspended) { // ignore first call, if start suspended
ConfigStartSuspended = 0;
return;
}
Resume();
if (MyVideoDecoder) {
if (VideoCodecID != CODEC_ID_NONE) {
@@ -906,9 +1118,7 @@ void SetPlayMode(void)
NewAudioStream = 1;
}
}
VideoFreezed = 0;
SkipAudio = 0;
SkipVideo = 0;
StreamFreezed = 0;
}
/**
@@ -933,9 +1143,9 @@ void Clear(void)
*/
void Play(void)
{
VideoFreezed = 0;
StreamFreezed = 0;
SkipAudio = 0;
// FIXME: restart audio
AudioPlay();
}
/**
@@ -943,13 +1153,15 @@ void Play(void)
*/
void Freeze(void)
{
VideoFreezed = 1;
// FIXME: freeze audio
AudioFlushBuffers();
StreamFreezed = 1;
AudioPause();
}
/**
** Display the given I-frame as a still picture.
**
** @param data pes frame data
** @param size number of bytes in frame
*/
void StillPicture(const uint8_t * data, int size)
{
@@ -962,10 +1174,10 @@ void StillPicture(const uint8_t * data, int size)
Error(_("[softhddev] invalid still video packet\n"));
return;
}
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
@@ -975,14 +1187,16 @@ void StillPicture(const uint8_t * data, int size)
const uint8_t *split;
int n;
// split the I-frame into single pes packets
if ((data[3] & 0xF0) == 0xE0) { // PES packet
split = data;
n = size;
// split the I-frame into single pes packets
do {
int len;
len = (split[4] << 8) + split[5];
if (len > n) {
if (!len || len + 6 > n) {
PlayVideo(split, n); // feed remaining bytes
break;
}
PlayVideo(split, len + 6); // feed it
@@ -992,11 +1206,23 @@ void StillPicture(const uint8_t * data, int size)
VideoNextPacket(VideoCodecID); // terminate last packet
if (VideoCodecID == CODEC_ID_H264) {
VideoEnqueue(AV_NOPTS_VALUE, seq_end_h264, sizeof(seq_end_h264));
VideoEnqueue(AV_NOPTS_VALUE, seq_end_h264,
sizeof(seq_end_h264));
} else {
VideoEnqueue(AV_NOPTS_VALUE, seq_end_mpeg, sizeof(seq_end_mpeg));
VideoEnqueue(AV_NOPTS_VALUE, seq_end_mpeg,
sizeof(seq_end_mpeg));
}
VideoNextPacket(VideoCodecID); // terminate last packet
} else { // ES packet
if (VideoCodecID != CODEC_ID_MPEG2VIDEO) {
VideoNextPacket(CODEC_ID_NONE); // close last stream
VideoCodecID = CODEC_ID_MPEG2VIDEO;
}
VideoEnqueue(AV_NOPTS_VALUE, data, size);
VideoEnqueue(AV_NOPTS_VALUE, seq_end_mpeg, sizeof(seq_end_mpeg));
VideoNextPacket(VideoCodecID); // terminate last packet
}
}
}
@@ -1079,19 +1305,22 @@ void OsdDrawARGB(int x, int y, int height, int width, const uint8_t * argb)
//////////////////////////////////////////////////////////////////////////////
static char ConfigStartX11Server; ///< flag start the x11 server
/**
** Return command line help string.
*/
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"
" -p device\taudio device for pass-through (hw:0,1 or /dev/dsp1)\n"
" -c channel\taudio mixer channel name (fe. PCM)\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"
" -x\t\tstart x11 server\n";
" -x\t\tstart x11 server\n" " -s\t\tstart in suspended mode\n"
" -w workaround\tenable/disable workarounds\n"
"\tno-hw-decoder\t\tdisable hw decoder, use software decoder only\n"
"\tno-mpeg-hw-decoder\tdisable hw decoder for mpeg only\n"
"\talsa-driver-broken\tdisable broken alsa driver message\n";
}
/**
@@ -1106,10 +1335,13 @@ int ProcessArgs(int argc, char *const argv[])
// Parse arguments.
//
for (;;) {
switch (getopt(argc, argv, "-a:p:d:fg:x")) {
case 'a': // audio device
switch (getopt(argc, argv, "-a:c:d:fg:p:sw:x")) {
case 'a': // audio device for pcm
AudioSetDevice(optarg);
continue;
case 'c': // channel of audio mixer
AudioSetChannel(optarg);
continue;
case 'p': // pass-through audio device
AudioSetDeviceAC3(optarg);
continue;
@@ -1130,6 +1362,20 @@ int ProcessArgs(int argc, char *const argv[])
case 'x': // x11 server
ConfigStartX11Server = 1;
continue;
case 's': // start in suspend mode
ConfigStartSuspended = 1;
continue;
case 'w': // workarounds
if (!strcasecmp("no-hw-decoder", optarg)) {
} else if (!strcasecmp("no-mpeg-hw-decoder", optarg)) {
} else if (!strcasecmp("alsa-driver-broken", optarg)) {
AudioAlsaDriverBroken = 1;
} else {
fprintf(stderr, _("Workaround '%s' unsupported\n"),
optarg);
return 0;
}
continue;
case EOF:
break;
case '-':
@@ -1261,7 +1507,7 @@ void SoftHdDeviceExit(void)
StopVideo();
CodecExit();
VideoPacketExit();
//VideoPacketExit();
if (ConfigStartX11Server) {
Debug(3, "x-setup: Stop x11 server\n");
@@ -1311,6 +1557,7 @@ void Start(void)
}
CodecInit();
if (!ConfigStartSuspended) {
// FIXME: AudioInit for HDMI after X11 startup
AudioInit();
MyAudioDecoder = CodecAudioNewDecoder();
@@ -1319,7 +1566,10 @@ void Start(void)
if (!ConfigStartX11Server) {
StartVideo();
}
} else {
SkipVideo = 1;
SkipAudio = 1;
}
pthread_mutex_init(&SuspendLockMutex, NULL);
}

View File

@@ -42,7 +42,7 @@ extern "C"
//////////////////////////////////////////////////////////////////////////////
static const char *const VERSION = "0.4.5";
static const char *const VERSION = "0.4.7";
static const char *const DESCRIPTION =
trNOOP("A software and GPU emulated HD device");
@@ -61,6 +61,9 @@ static const char *const Resolution[RESOLUTIONS] = {
static char ConfigMakePrimary; ///< config primary wanted
static char ConfigHideMainMenuEntry; ///< config hide main menu entry
static int ConfigVideoSkipLines; ///< config skip lines top/bottom
static int ConfigVideoStudioLevels; ///< config use studio levels
/// config deinterlace
static int ConfigVideoDeinterlace[RESOLUTIONS];
@@ -77,7 +80,6 @@ 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
@@ -147,9 +149,34 @@ class cSoftOsd:public cOsd
cSoftOsd(int, int, uint);
virtual ~ cSoftOsd(void);
virtual void Flush(void);
// virtual void SetActive(bool);
virtual void SetActive(bool);
};
static volatile char OsdDirty; ///< flag force redraw everything
/**
** Sets this OSD to be the active one.
**
** @param on true on, false off
**
** @note only needed as workaround for text2skin plugin with
** undrawn areas.
*/
void cSoftOsd::SetActive(bool on)
{
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, on);
if (Active() == on) {
return; // already active, no action
}
cOsd::SetActive(on);
if (on) {
OsdDirty = 1;
} else {
OsdClose();
}
}
cSoftOsd::cSoftOsd(int left, int top, uint level)
:cOsd(left, top, level)
{
@@ -166,6 +193,7 @@ cSoftOsd::~cSoftOsd(void)
{
//dsyslog("[softhddev]%s:\n", __FUNCTION__);
SetActive(false);
// done by SetActive: OsdClose();
#ifdef USE_YAEPG
// support yaepghd, video window
@@ -179,12 +207,11 @@ cSoftOsd::~cSoftOsd(void)
VideoSetOutputPosition(0, 0, width, height);
}
#endif
OsdClose();
}
///
/// Actually commits all data to the OSD hardware.
///
/**
** Actually commits all data to the OSD hardware.
*/
void cSoftOsd::Flush(void)
{
cPixmapMemory *pm;
@@ -227,43 +254,32 @@ void cSoftOsd::Flush(void)
int y2;
// get dirty bounding box
if (!bitmap->Dirty(x1, y1, x2, y2)) {
if (OsdDirty) { // forced complete update
x1 = 0;
y1 = 0;
x2 = bitmap->Width() - 1;
y2 = bitmap->Height() - 1;
} else if (!bitmap->Dirty(x1, y1, x2, y2)) {
continue; // nothing dirty continue
}
#if 0
// FIXME: need only to convert and upload dirty areas
// DrawBitmap(bitmap);
w = bitmap->Width();
h = bitmap->Height();
argb = (uint8_t *) malloc(w * h * sizeof(uint32_t));
for (y = 0; y < h; ++y) {
for (x = 0; x < w; ++x) {
((uint32_t *) argb)[x + y * w] = bitmap->GetColor(x, y);
}
}
// check if subtitles
if (this->Level == OSD_LEVEL_SUBTITLES) {
int video_width;
int video_height;
if (0) {
dsyslog("[softhddev]%s: subtitle %d, %d\n", __FUNCTION__,
Left() + bitmap->X0(), Top() + bitmap->Y0());
}
video_width = 1920;
video_height = 1080;
OsdDrawARGB((1920 - video_width) / 2 + Left() + bitmap->X0(),
1080 - video_height + Top() + bitmap->Y0(), w, h, argb);
} else {
OsdDrawARGB(Left() + bitmap->X0(), Top() + bitmap->Y0(), w, h,
argb);
}
#else
// convert and upload only dirty areas
w = x2 - x1 + 1;
h = y2 - y1 + 1;
if (1) { // just for the case it makes trouble
int width;
int height;
double video_aspect;
::GetOsdSize(&width, &height, &video_aspect);
if (w > width) {
w = width;
x2 = x1 + width - 1;
}
if (h > height) {
h = height;
y2 = y1 + height - 1;
}
}
#ifdef DEBUG
if (w > bitmap->Width() || h > bitmap->Height()) {
esyslog(tr("softhdev: dirty area too big\n"));
@@ -277,14 +293,14 @@ void cSoftOsd::Flush(void)
bitmap->GetColor(x, y);
}
}
// check if subtitles
OsdDrawARGB(Left() + bitmap->X0() + x1, Top() + bitmap->Y0() + y1,
w, h, argb);
#endif
bitmap->Clean();
// FIXME: reuse argb
free(argb);
}
OsdDirty = 0;
return;
}
@@ -329,6 +345,10 @@ cOsd *cSoftOsdProvider::Osd; ///< single osd
/**
** Create a new OSD.
**
** @param left x-coordinate of OSD
** @param top y-coordinate of OSD
** @param level layer level of OSD
*/
cOsd *cSoftOsdProvider::CreateOsd(int left, int top, uint level)
{
@@ -364,6 +384,8 @@ class cMenuSetupSoft:public cMenuSetupPage
protected:
int MakePrimary;
int HideMainMenuEntry;
int SkipLines;
int StudioLevels;
int Scaling[RESOLUTIONS];
int Deinterlace[RESOLUTIONS];
int SkipChromaDeinterlace[RESOLUTIONS];
@@ -403,7 +425,8 @@ static inline cOsdItem *SeparatorItem(const char *label)
cMenuSetupSoft::cMenuSetupSoft(void)
{
static const char *const deinterlace[] = {
"Bob", "Weave/None", "Temporal", "TemporalSpatial", "Software"
"Bob", "Weave/None", "Temporal", "TemporalSpatial", "Software Bob",
"Software Spatial",
};
static const char *const scaling[] = {
"Normal", "Fast", "HQ", "Anamorphic"
@@ -428,12 +451,20 @@ cMenuSetupSoft::cMenuSetupSoft(void)
// video
//
Add(SeparatorItem(tr("Video")));
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")));
for (i = 0; i < RESOLUTIONS; ++i) {
Add(SeparatorItem(resolution[i]));
Scaling[i] = ConfigVideoScaling[i];
Add(new cMenuEditStraItem(tr("Scaling"), &Scaling[i], 4, scaling));
Deinterlace[i] = ConfigVideoDeinterlace[i];
Add(new cMenuEditStraItem(tr("Deinterlace"), &Deinterlace[i], 5,
Add(new cMenuEditStraItem(tr("Deinterlace"), &Deinterlace[i], 6,
deinterlace));
SkipChromaDeinterlace[i] = ConfigVideoSkipChromaDeinterlace[i];
Add(new cMenuEditBoolItem(tr("SkipChromaDeinterlace (vdpau)"),
@@ -491,6 +522,11 @@ void cMenuSetupSoft::Store(void)
SetupStore("HideMainMenuEntry", ConfigHideMainMenuEntry =
HideMainMenuEntry);
SetupStore("SkipLines", ConfigVideoSkipLines = SkipLines);
VideoSetSkipLines(ConfigVideoSkipLines);
SetupStore("StudioLevels", ConfigVideoStudioLevels = StudioLevels);
VideoSetStudioLevels(ConfigVideoStudioLevels);
for (i = 0; i < RESOLUTIONS; ++i) {
char buf[128];
@@ -520,8 +556,10 @@ void cMenuSetupSoft::Store(void)
SetupStore("AutoCrop.Interval", ConfigAutoCropInterval = AutoCropInterval);
SetupStore("AutoCrop.Delay", ConfigAutoCropDelay = AutoCropDelay);
SetupStore("AutoCrop.Tolerance", ConfigAutoCropTolerance = AutoCropTolerance);
VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay, ConfigAutoCropTolerance);
SetupStore("AutoCrop.Tolerance", ConfigAutoCropTolerance =
AutoCropTolerance);
VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay,
ConfigAutoCropTolerance);
SetupStore("Suspend.Close", ConfigSuspendClose = SuspendClose);
SetupStore("Suspend.X11", ConfigSuspendX11 = SuspendX11);
@@ -560,9 +598,8 @@ cSoftHdPlayer::~cSoftHdPlayer()
*/
class cSoftHdControl:public cControl
{
private:
cSoftHdPlayer * Player;
public:
static cSoftHdPlayer *Player; ///< dummy player
virtual void Hide(void)
{
}
@@ -573,6 +610,13 @@ class cSoftHdControl:public cControl
virtual ~ cSoftHdControl();
};
cSoftHdPlayer *cSoftHdControl::Player;
/**
** Handle a key event.
**
** @param key key pressed
*/
eOSState cSoftHdControl::ProcessKey(eKeys key)
{
if (!ISMODELESSKEY(key) || key == kBack || key == kStop) {
@@ -580,8 +624,8 @@ eOSState cSoftHdControl::ProcessKey(eKeys key)
delete Player;
Player = NULL;
Resume();
}
Resume();
return osEnd;
}
return osContinue;
@@ -620,11 +664,11 @@ class cSoftHdDevice:public cDevice
virtual void Play(void);
virtual void Freeze(void);
virtual void Mute(void);
virtual void SetVolumeDevice(int);
virtual void StillPicture(const uchar *, int);
virtual bool Poll(cPoller &, int = 0);
virtual bool Flush(int = 0);
virtual int64_t GetSTC(void);
virtual void SetVideoDisplayFormat(eVideoDisplayFormat);
virtual void GetVideoSize(int &, int &, double &);
virtual void GetOsdSize(int &, int &, double &);
virtual int PlayVideo(const uchar *, int);
@@ -637,6 +681,7 @@ class cSoftHdDevice:public cDevice
virtual int GetAudioChannelDevice(void);
virtual void SetDigitalAudioDevice(bool);
virtual void SetAudioTrackDevice(eTrackType);
virtual void SetVolumeDevice(int);
virtual int PlayAudio(const uchar *, int, uchar);
// Image Grab facilities
@@ -662,6 +707,7 @@ cSoftHdDevice::cSoftHdDevice(void)
#if 0
spuDecoder = NULL;
#endif
SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
}
cSoftHdDevice::~cSoftHdDevice(void)
@@ -740,6 +786,10 @@ bool cSoftHdDevice::SetPlayMode(ePlayMode play_mode)
return true;
}
/**
** Gets the current System Time Counter, which can be used to
** synchronize audio, video and subtitles.
*/
int64_t cSoftHdDevice::GetSTC(void)
{
//dsyslog("[softhddev]%s:\n", __FUNCTION__);
@@ -784,6 +834,9 @@ void cSoftHdDevice::Freeze(void)
::Freeze();
}
/**
** Turns off audio while replaying.
*/
void cSoftHdDevice::Mute(void)
{
dsyslog("[softhddev]%s:\n", __FUNCTION__);
@@ -792,13 +845,6 @@ void cSoftHdDevice::Mute(void)
::Mute();
}
void cSoftHdDevice::SetVolumeDevice(int volume)
{
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, volume);
::SetVolumeDevice(volume);
}
/**
** Display the given I-frame as a still picture.
*/
@@ -843,6 +889,30 @@ bool cSoftHdDevice::Flush(int timeout_ms)
// ----------------------------------------------------------------------------
/**
** Sets the video display format to the given one (only useful if this
** device has an MPEG decoder).
**
** @note this function isn't called on the initial channel
*/
void cSoftHdDevice::SetVideoDisplayFormat(
eVideoDisplayFormat video_display_format)
{
static int last = -1;
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, video_display_format);
cDevice::SetVideoDisplayFormat(video_display_format);
// called on every channel switch, no need to kill osd...
if (last != video_display_format) {
last = video_display_format;
::VideoSetDisplayFormat(video_display_format);
OsdDirty = 1;
}
}
/**
** Returns the width, height and video_aspect ratio of the currently
** displayed video material.
@@ -899,11 +969,23 @@ int cSoftHdDevice::GetAudioChannelDevice(void)
return 0;
}
/**
** Sets the audio volume on this device (Volume = 0...255).
**
** @param volume device volume
*/
void cSoftHdDevice::SetVolumeDevice(int volume)
{
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, volume);
::SetVolumeDevice(volume);
}
// ----------------------------------------------------------------------------
///
/// Play a video packet.
///
/**
** Play a video packet.
*/
int cSoftHdDevice::PlayVideo(const uchar * data, int length)
{
//dsyslog("[softhddev]%s: %p %d\n", __FUNCTION__, data, length);
@@ -958,6 +1040,16 @@ uchar *cSoftHdDevice::GrabImage(int &size, bool jpeg, int quality, int width,
return::GrabImage(&size, jpeg, quality, width, height);
}
/**
** Call rgb to jpeg for C Plugin.
*/
extern "C" uint8_t * CreateJpeg(uint8_t * image, int *size, int quality,
int width, int height)
{
return (uint8_t *) RgbToJpeg((uchar *) image, width, height, *size,
quality);
}
//////////////////////////////////////////////////////////////////////////////
// cPlugin
//////////////////////////////////////////////////////////////////////////////
@@ -1103,6 +1195,7 @@ cOsdObject *cPluginSoftHdDevice::MainMenuAction(void)
//dsyslog("[softhddev]%s:\n", __FUNCTION__);
//MyDevice->StopReplay();
if (!cSoftHdControl::Player) { // not already suspended
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
@@ -1110,6 +1203,7 @@ cOsdObject *cPluginSoftHdDevice::MainMenuAction(void)
dsyslog("[softhddev]%s: set user inactive\n", __FUNCTION__);
ShutdownHandler.SetUserInactive();
}
}
return NULL;
}
@@ -1168,6 +1262,14 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
ConfigHideMainMenuEntry = atoi(value);
return true;
}
if (!strcmp(name, "SkipLines")) {
VideoSetSkipLines(ConfigVideoSkipLines = atoi(value));
return true;
}
if (!strcmp(name, "StudioLevels")) {
VideoSetStudioLevels(ConfigVideoStudioLevels = atoi(value));
return true;
}
for (i = 0; i < RESOLUTIONS; ++i) {
char buf[128];
@@ -1204,10 +1306,6 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
}
}
if (!strcmp(name, "SkipLines")) {
VideoSetSkipLines(ConfigVideoSkipLines = atoi(value));
return true;
}
if (!strcmp(name, "AudioDelay")) {
VideoSetAudioDelay(ConfigVideoAudioDelay = atoi(value));
return true;
@@ -1228,7 +1326,8 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
return true;
}
if (!strcmp(name, "AutoCrop.Tolerance")) {
VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay, ConfigAutoCropTolerance = atoi(value));
VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay,
ConfigAutoCropTolerance = atoi(value));
return true;
}
@@ -1268,10 +1367,8 @@ const char **cPluginSoftHdDevice::SVDRPHelpPages(void)
{
// FIXME: translation?
static const char *text[] = {
"SUSP\n"
" Suspend plugin.\n",
"RESU\n"
" Resume plugin.\n",
"SUSP\n" " Suspend plugin.\n",
"RESU\n" " Resume plugin.\n",
NULL
};
@@ -1286,6 +1383,10 @@ cString cPluginSoftHdDevice::SVDRPCommand(const char *command,
__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
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
@@ -1295,8 +1396,10 @@ cString cPluginSoftHdDevice::SVDRPCommand(const char *command,
if (ShutdownHandler.GetUserInactiveTime()) {
ShutdownHandler.SetUserInactiveTimeout();
}
Resume();
if (cSoftHdControl::Player) { // suspended
cControl::Shutdown(); // not need, if not suspended
}
Resume();
return "SoftHdDevice is resumed";
}
return NULL;

View File

@@ -23,19 +23,19 @@ SLOT="0"
KEYWORDS="~x86 ~amd64"
IUSE="vaapi vdpau alsa oss yaepg opengl jpeg"
DEPEND=">=x11-libs/libxcb-1.7
DEPEND=">=x11-libs/libxcb-1.8
x11-libs/xcb-util
x11-libs/xcb-util-wm
x11-libs/xcb-util-keysyms
x11-libs/xcb-util-renderutil
x11-libs/libX11
opengl? ( virtual/opengl )
>=media-video/ffmpeg-0.7
>=virtual/ffmpeg-0.7
sys-devel/gettext
sys-devel/make
dev-util/pkgconfig
yaepg? ( >=media-video/vdr-1.7[yaepg] )
!yaepg? ( >=media-video/vdr-1.7 )
yaepg? ( >=media-video/vdr-1.7.23[yaepg] )
!yaepg? ( >=media-video/vdr-1.7.23 )
vdpau? ( x11-libs/libvdpau )
vaapi? ( x11-libs/libva )
alsa? ( media-libs/alsa-lib )
@@ -64,10 +64,11 @@ src_compile() {
src_install() {
vdr-plugin_src_install
dodir /etc/vdr/plugins || die
dodoc README.txt
insinto /etc/vdr/plugins
fowners -R vdr:vdr /etc/vdr || die
#dodir /etc/vdr/plugins || die
#insinto /etc/vdr/plugins
#fowners -R vdr:vdr /etc/vdr || die
#insinto /etc/conf.d
#doins vdr.softhddevice

2028
video.c

File diff suppressed because it is too large Load Diff

19
video.h
View File

@@ -47,16 +47,17 @@ extern unsigned VideoGetSurface(VideoHwDecoder *);
extern void VideoReleaseSurface(VideoHwDecoder *, unsigned);
#ifdef LIBAVCODEC_VERSION
/// Render a ffmpeg frame.
extern void VideoRenderFrame(VideoHwDecoder *, AVCodecContext *, AVFrame *);
/// Get ffmpeg vaapi context.
extern struct vaapi_context *VideoGetVaapiContext(VideoHwDecoder *);
/// Callback to negotiate the PixelFormat.
extern enum PixelFormat Video_get_format(VideoHwDecoder *, AVCodecContext *,
const enum PixelFormat *);
/// Render a ffmpeg frame.
extern void VideoRenderFrame(VideoHwDecoder *, const AVCodecContext *,
const AVFrame *);
/// Get ffmpeg vaapi context.
extern struct vaapi_context *VideoGetVaapiContext(VideoHwDecoder *);
#ifdef AVCODEC_VDPAU_H
/// Draw vdpau render state.
extern void VideoDrawRenderState(VideoHwDecoder *,
@@ -79,6 +80,9 @@ extern void VideoSetOutputPosition(int, int, int, int);
/// Set video mode.
extern void VideoSetVideoMode(int, int, int, int);
/// Set display format.
extern void VideoSetDisplayFormat(int);
/// Set video fullscreen mode.
extern void VideoSetFullscreen(int);
@@ -100,6 +104,9 @@ extern void VideoSetSharpen(int[]);
/// Set skip lines.
extern void VideoSetSkipLines(int);
/// Set studio levels.
extern void VideoSetStudioLevels(int);
/// Set audio delay.
extern void VideoSetAudioDelay(int);