mirror of
https://projects.vdr-developer.org/git/vdr-plugin-softhddevice.git
synced 2023-10-10 17:16:51 +00:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e10e62dcf7 | ||
|
|
2a1793c98e | ||
|
|
30d4586448 | ||
|
|
aa4debc9c8 | ||
| ac2e10a308 | |||
|
|
c986d285ea | ||
|
|
8612044b9b | ||
|
|
c19b86411a | ||
|
|
9165052d5e | ||
|
|
413983a666 | ||
|
|
f86fa4edd7 | ||
|
|
7f8110557f | ||
|
|
c9b344a3fd | ||
|
|
b41f934c37 | ||
|
|
6058f3da56 | ||
| 689d75b808 | |||
|
|
bd4503f30b | ||
|
|
24ba8175a3 | ||
|
|
fe24cbb182 | ||
|
|
6eff8fa818 | ||
|
|
552a994db3 | ||
|
|
d24f19bc2d | ||
|
|
7b570c507c | ||
|
|
09ba3e2993 | ||
|
|
d0f825f831 | ||
|
|
47d2896468 | ||
|
|
f59425ac57 | ||
|
|
1acdeee913 | ||
|
|
c2938c7ef3 | ||
|
|
d65fe88c83 | ||
|
|
7d3f4f4434 | ||
|
|
acc35fe30c | ||
|
|
ee5804fed7 | ||
|
|
1cbaddf75c | ||
|
|
226760490b | ||
|
|
7931909e28 | ||
|
|
129c139ed7 | ||
|
|
340816d763 | ||
|
|
d6c6818ecf | ||
|
|
181a0bb372 | ||
|
|
f2d4163899 | ||
|
|
4cc98d7937 | ||
|
|
3812fa8d38 | ||
|
|
da5c5cd5fd | ||
|
|
74a62e3649 | ||
|
|
7e1a42f7ed | ||
|
|
dda9011abc | ||
|
|
de79e9211f | ||
|
|
b0d9f41020 | ||
|
|
4d1a516c80 | ||
|
|
995f1286bd | ||
|
|
fd0ae12f24 | ||
|
|
db258a0fbd | ||
|
|
0df8e8a5fc | ||
|
|
6a28064dce | ||
|
|
b5e9077c74 | ||
|
|
3b4ace14cf | ||
|
|
5aa868c296 | ||
|
|
43b48224b5 | ||
|
|
144f22314f | ||
|
|
51eb720265 | ||
|
|
e977007dd3 | ||
|
|
769f00b4f6 | ||
|
|
aa426cd8b2 | ||
|
|
b2cab00599 | ||
|
|
b54d62ef35 | ||
|
|
9b68248a3e | ||
|
|
762959fbb4 | ||
|
|
07b426f2b5 | ||
|
|
668a6ec277 | ||
|
|
82f61de117 | ||
|
|
67e571f02b | ||
|
|
c17af0e958 | ||
|
|
2561214c3e | ||
|
|
7382bd60ff | ||
|
|
73b93f1aba | ||
|
|
0243b1c8a7 | ||
|
|
6ce760ccd8 | ||
|
|
2f869884ba | ||
|
|
5d8dea1b6b | ||
|
|
1f232db5b4 | ||
|
|
c4ad13c53f | ||
|
|
98f73f2199 | ||
|
|
89ca44206c | ||
|
|
5c9b85b69b | ||
|
|
09cfab3856 | ||
|
|
30e903d90a | ||
|
|
852d367225 |
65
ChangeLog
65
ChangeLog
@@ -1,3 +1,68 @@
|
||||
User johns
|
||||
Date: Sat Apr 7 20:21:16 CEST 2012
|
||||
|
||||
Release Version 0.5.0
|
||||
Change audio/video delay with hot-key.
|
||||
Enable/disable/toggle fullscreen with hot-key (Feature #930).
|
||||
|
||||
User: CafeDelMar
|
||||
Date: Thu Apr 5 22:44:06 CEST 2012
|
||||
|
||||
Cutting pixels are now configured for each resolution.
|
||||
|
||||
User johns
|
||||
Date: Thu Apr 5 15:47:59 CEST 2012
|
||||
|
||||
Buffer less video and audio.
|
||||
Fix 100% cpu use, with mp3 plugin.
|
||||
Audio/Video sync rewrite, trick-speed support moved to video.
|
||||
Faster VdpauBlackSurface version.
|
||||
Fix bug: VideoSetPts wrong position for multi frame packets.
|
||||
|
||||
User: CafeDelMar
|
||||
Date: Mon Mar 26 20:45:54 CEST 2012
|
||||
|
||||
Add VideoSkipPixels support.
|
||||
|
||||
User johns
|
||||
Date: Fri Mar 23 18:43:20 CET 2012
|
||||
|
||||
Add optional argument (display) to ATTA svdrp commmand.
|
||||
Wakeup display to show OSD for remote learning mode.
|
||||
Support switching the primary device with svdrp.
|
||||
Disable and reenable screen saver and DPMS.
|
||||
Video source code cleanup.
|
||||
Fix fast backward with some h264 streams.
|
||||
Make soft start sync setup menu configurable.
|
||||
Fix bug: StillPicture NAL end of sequence is 10 and not 0x10.
|
||||
Fix bug: AudioEnqueue crash without sound card.
|
||||
|
||||
User johns
|
||||
Date: Sun Mar 4 22:35:36 CET 2012
|
||||
|
||||
Release Version 0.4.9
|
||||
Experimental ac3 audio drift correction support.
|
||||
Removes LPCM detection from TS parser.
|
||||
Rewrote video/audio start code.
|
||||
Add support for attach/detach plugin.
|
||||
OSS needs bigger audio buffers.
|
||||
Improved audio drift correction support.
|
||||
Experimental audio drift correction support.
|
||||
Add SVDRP HOTK command support.
|
||||
Increased audio buffer time for PES packets.
|
||||
Support configuration and set of video background.
|
||||
Survive lost X11 display.
|
||||
Fix bug: 100% cpu use with plugins like mp3.
|
||||
Wakeup display thread on channel switch, osd can now be shown without
|
||||
video.
|
||||
Makes 60Hz display mode configurable with setup.conf.
|
||||
Support downmix of AC-3 to stero.
|
||||
New audio PES packet parser.
|
||||
Fix bug: Grabbing a JPG image fails while suspended.
|
||||
Add support for hot keys.
|
||||
Add support to use characters input in edit mode.
|
||||
Adds trick speed support.
|
||||
|
||||
User johns
|
||||
Date: Thu Feb 16 09:59:14 CET 2012
|
||||
|
||||
|
||||
14
Makefile
14
Makefile
@@ -19,8 +19,12 @@ 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 += -DUSE_AUDIO_DRIFT_CORRECTION # build new audio drift code
|
||||
#CONFIG += -DUSE_AC3_DRIFT_CORRECTION # build new ac-3 drift code
|
||||
CONFIG += -DAV_INFO -DAV_INFO_TIME=3000 # debug a/v sync
|
||||
#CONFIG += -DHAVE_PTHREAD_NAME # supports new pthread_setname_np
|
||||
#CONFIG += -DNO_TS_AUDIO # disable ts audio parser
|
||||
#CONFIG += -DUSE_TS_VIDEO # build new ts video parser
|
||||
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")
|
||||
@@ -33,7 +37,7 @@ CXX ?= g++
|
||||
CFLAGS ?= -g -O2 -W -Wall -Wextra -Winit-self \
|
||||
-Wdeclaration-after-statement \
|
||||
-ftree-vectorize -msse3 -flax-vector-conversions
|
||||
CXXFLAGS ?= -g -O2 -W -Wall -Wextra -Woverloaded-virtual
|
||||
CXXFLAGS ?= -g -O2 -W -Wall -Wextra -Werror=overloaded-virtual
|
||||
|
||||
### The directory environment:
|
||||
|
||||
@@ -66,7 +70,7 @@ DEFINES += $(CONFIG) -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' \
|
||||
$(if $(GIT_REV), -DGIT_REV='"$(GIT_REV)"')
|
||||
|
||||
_CFLAGS = $(DEFINES) $(INCLUDES) \
|
||||
$(shell pkg-config --cflags libavcodec libavformat) \
|
||||
$(shell pkg-config --cflags libavcodec) \
|
||||
`pkg-config --cflags x11 x11-xcb xcb xcb-xv xcb-shm xcb-dpms xcb-atom\
|
||||
xcb-screensaver xcb-randr xcb-glx xcb-icccm xcb-keysyms`\
|
||||
`pkg-config --cflags gl glu` \
|
||||
@@ -82,7 +86,7 @@ override CXXFLAGS += $(_CFLAGS)
|
||||
override CFLAGS += $(_CFLAGS)
|
||||
|
||||
LIBS += -lrt \
|
||||
$(shell pkg-config --libs libavcodec libavformat) \
|
||||
$(shell pkg-config --libs libavcodec) \
|
||||
`pkg-config --libs x11 x11-xcb xcb xcb-xv xcb-shm xcb-dpms xcb-atom\
|
||||
xcb-screensaver xcb-randr xcb-glx xcb-icccm xcb-keysyms`\
|
||||
`pkg-config --libs gl glu` \
|
||||
|
||||
80
README.txt
80
README.txt
@@ -20,23 +20,24 @@ $Id$
|
||||
|
||||
A software and GPU emulated HD output device plugin for VDR.
|
||||
|
||||
o Video VA-API/VA-API (with intel, nvidia and amd backend supported)
|
||||
o Video CPU/VA-API
|
||||
o Video VDPAU/VDPAU
|
||||
o Video CPU/VDPAU
|
||||
o Audio FFMpeg/Alsa/Analog
|
||||
o Audio FFMpeg/Alsa/Digital
|
||||
o Audio FFMpeg/OSS/Analog
|
||||
o HDMI/SPDIF Passthrough
|
||||
o VA-API bob software deinterlace
|
||||
o Auto-crop
|
||||
o Video decoder CPU / VA-API / VDPAU
|
||||
o Video output VA-API / VDPAU
|
||||
o Audio FFMpeg / Alsa / Analog
|
||||
o Audio FFMpeg / Alsa / Digital
|
||||
o Audio FFMpeg / OSS / Analog
|
||||
o HDMI/SPDIF pass-through
|
||||
o YaepgHD support
|
||||
o Software deinterlacer Bob (VA-API only)
|
||||
o Autocrop
|
||||
o Grab image (VDPAU only)
|
||||
o Suspend
|
||||
o Letterbox, Stretch and Center cut-out video display modes
|
||||
|
||||
o planned: Video VA-API/Opengl
|
||||
o planned: Video VDPAU/Opengl
|
||||
o planned: Video CPU/Xv
|
||||
o planned: Video CPU/Opengl
|
||||
o planned: Video decoder VA-API Branch: vaapi-ext/staging
|
||||
o planned: Video output XvBA / Opengl / Xv
|
||||
o planned: VA-API grab image
|
||||
o planned: Improved Software Deinterlacer (yadif or/and ffmpeg filters)
|
||||
o planned: Video XvBA/XvBA
|
||||
o planned: software volume, software channel resample
|
||||
o planned: atmo light support
|
||||
|
||||
To compile you must have the 'requires' installed.
|
||||
@@ -137,14 +138,25 @@ Setup: /etc/vdr/setup.conf
|
||||
-1000 .. 1000 noise reduction level (0 off, -1000 max blur,
|
||||
1000 max sharp)
|
||||
|
||||
softhddevice.<res>.CutTopBottom = 0
|
||||
Cut 'n' pixels at at top and bottom of the video picture.
|
||||
|
||||
softhddevice.<res>.CutLeftRight = 0
|
||||
Cut 'n' pixels at at left and right of the video picture.
|
||||
|
||||
softhddevice.AudioDelay = 0
|
||||
+n or -n ms
|
||||
delay audio or delay video
|
||||
|
||||
softhddevice.AudioPassthrough = 0
|
||||
0 = none, 1 = AC-3
|
||||
|
||||
for AC-3 the pass-through device is used.
|
||||
|
||||
softhddevice.AudioDownmix = 0
|
||||
0 = none, 1 = downmix
|
||||
downmix AC-3 to stero.
|
||||
|
||||
softhddevice.AutoCrop.Interval = 0
|
||||
0 disables auto-crop
|
||||
n each 'n' frames auto-crop is checked.
|
||||
@@ -157,8 +169,13 @@ 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.Background = 0
|
||||
32bit RGBA background color
|
||||
(Red * 16777216 + Green * 65536 + Blue * 256 + Alpha)
|
||||
or hex RRGGBBAA
|
||||
grey 127 * 16777216 + 127 * 65536 + 127 * 256 => 2139062016
|
||||
in the setup menu this is entered as (24bit RGB and 8bit Alpha)
|
||||
(Red * 65536 + Green * 256 + Blue)
|
||||
|
||||
softhddevice.StudioLevels = 0
|
||||
0 use PC levels (0-255) with vdpau.
|
||||
@@ -171,6 +188,14 @@ Setup: /etc/vdr/setup.conf
|
||||
softhddevice.Suspend.X11 = 0
|
||||
1 suspend stops X11 server (not working yet)
|
||||
|
||||
softhddevice.60HzMode = 0
|
||||
0 disable 60Hz display mode
|
||||
1 enable 60Hz display mode
|
||||
|
||||
softhddevice.SoftStartSync = 0
|
||||
0 disable soft start of audio/video sync
|
||||
1 enable soft start of audio/video sync
|
||||
|
||||
VideoDisplayFormat = ?
|
||||
0 pan and scan
|
||||
1 letter box
|
||||
@@ -209,8 +234,20 @@ Commandline:
|
||||
SVDRP:
|
||||
------
|
||||
|
||||
Use 'svdrpsend.pl plug softhddevice HELP' to see the SVDRP commands
|
||||
help and which are supported by the plugin.
|
||||
Use 'svdrpsend.pl plug softhddevice HELP'
|
||||
or 'svdrpsend plug softhddevice HELP' to see the SVDRP commands help
|
||||
and which are supported by the plugin.
|
||||
|
||||
Keymacros:
|
||||
----------
|
||||
|
||||
See keymacros.conf how to setup the macros.
|
||||
|
||||
This are the supported key sequences:
|
||||
|
||||
@softhddevice Blue 1 0 disable pass-through
|
||||
@softhddevice Blue 1 1 enable pass-through
|
||||
@softhddevice Blue 1 2 toggle pass-through
|
||||
|
||||
Running:
|
||||
--------
|
||||
@@ -248,6 +285,11 @@ Requires:
|
||||
x11-libs/xvba-video
|
||||
XVBA Backend for Video Acceleration (VA) API
|
||||
http://www.freedesktop.org/wiki/Software/vaapi
|
||||
|
||||
x11-libs/libvdpau
|
||||
VDPAU wrapper and trace libraries
|
||||
http://www.freedesktop.org/wiki/Software/VDPAU
|
||||
|
||||
x11-libs/libxcb,
|
||||
X C-language Bindings library
|
||||
http://xcb.freedesktop.org
|
||||
|
||||
35
Todo
35
Todo
@@ -19,26 +19,24 @@ GNU Affero General Public License for more details.
|
||||
$Id: $
|
||||
|
||||
missing:
|
||||
software deinterlace (yadif, ...)
|
||||
software decoder with software deinterlace
|
||||
more software deinterlace (yadif, ...)
|
||||
more software decoder with software deinterlace
|
||||
suspend output / energie saver: stop and restart X11
|
||||
suspend plugin didn't restore full-screen (is this wanted?)
|
||||
Option deinterlace off / deinterlace force!
|
||||
ColorSpace aren't configurable with the gui.
|
||||
Inverse telecine isn't configurable with the gui.
|
||||
|
||||
crash:
|
||||
AudioPlayHandlerThread -> pthread_cond_wait
|
||||
works for me: restart vdr not working, when started x11 was killed.
|
||||
|
||||
video:
|
||||
subtitle not cleared
|
||||
subtitle could be asyncron
|
||||
reduce warnings after channel switch
|
||||
grab image with hardware and better scaling support
|
||||
hard channel switch
|
||||
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
|
||||
radio show black background
|
||||
radio no need to wait on video buffers
|
||||
starting with radio and own X11 server, shows no video
|
||||
some low-bandwidth tv channels have hiccups.
|
||||
|
||||
vdpau:
|
||||
software decoder path not working
|
||||
@@ -52,8 +50,8 @@ libva:
|
||||
[drm:i915_hangcheck_elapsed] *ERROR* Hangcheck timer elapsed... GPU hung
|
||||
[drm:i915_wait_request] *ERROR* i915_wait_request returns -11 ...
|
||||
|
||||
libva: branch vaapi-ext
|
||||
add support for vaapi-ext
|
||||
libva: branch vaapi-ext / staging
|
||||
add support for vaapi-ext / staging
|
||||
|
||||
libva-intel-driver:
|
||||
deinterlace only supported with vaapi-ext
|
||||
@@ -71,18 +69,21 @@ libva-vdpau-driver:
|
||||
libva-xvba-driver:
|
||||
|
||||
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 (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.
|
||||
Relaxed audio sync checks at end of packet and already in sync
|
||||
samplerate problem resume/suspend.
|
||||
only wait for video start, if video is running.
|
||||
Not primary device, don't use and block audio/video.
|
||||
multiple open of audio device, reduce them.
|
||||
|
||||
audio/alsa:
|
||||
better downmix of >2 channels on 2 channel hardware
|
||||
@@ -99,8 +100,11 @@ HDMI/SPDIF Passthrough:
|
||||
only AC-3 written
|
||||
|
||||
playback of recording
|
||||
pause is not reset, when replay exit
|
||||
replay/pause need 100% cpu
|
||||
pause is not reset, when replay exit (fixed?)
|
||||
replay/pause need 100% cpu (fixed?)
|
||||
|
||||
plugins:
|
||||
mp3 plugin needs 100% cpu (bad ::Poll)
|
||||
|
||||
setup:
|
||||
Setup of decoder type.
|
||||
@@ -112,6 +116,7 @@ setup:
|
||||
|
||||
unsorted:
|
||||
stoping vdr while plugin is suspended opens and closes a window.
|
||||
svdrp prim: support plugin names for device numbers.
|
||||
|
||||
future features (not planed for 1.0 - 1.5)
|
||||
|
||||
|
||||
470
audio.c
470
audio.c
@@ -105,18 +105,20 @@ typedef struct _audio_module_
|
||||
{
|
||||
const char *Name; ///< audio output module name
|
||||
|
||||
void (*Thread) (void); ///< module thread handler
|
||||
void (*Enqueue) (const void *, int); ///< enqueue samples for output
|
||||
void (*FlushBuffers) (void); ///< flush sample buffers
|
||||
void (*Poller) (void); ///< output poller
|
||||
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 *, int); ///< setup channels, samplerate
|
||||
void (*Play) (void); ///< play
|
||||
void (*Pause) (void); ///< pause
|
||||
void (*Init) (void); ///< initialize audio output module
|
||||
void (*Exit) (void); ///< cleanup audio output module
|
||||
void (*const Thread) (void); ///< module thread handler
|
||||
void (*const Enqueue) (const void *, int); ///< enqueue samples for output
|
||||
void (*const VideoReady) (void); ///< video ready, start audio
|
||||
void (*const FlushBuffers) (void); ///< flush sample buffers
|
||||
void (*const Poller) (void); ///< output poller
|
||||
int (*const FreeBytes) (void); ///< number of bytes free in buffer
|
||||
int (*const UsedBytes) (void); ///< number of bytes used in buffer
|
||||
int64_t(*const GetDelay) (void); ///< get current audio delay
|
||||
void (*const SetVolume) (int); ///< set output volume
|
||||
int (*const Setup) (int *, int *, int); ///< setup channels, samplerate
|
||||
void (*const Play) (void); ///< play
|
||||
void (*const Pause) (void); ///< pause
|
||||
void (*const Init) (void); ///< initialize audio output module
|
||||
void (*const Exit) (void); ///< cleanup audio output module
|
||||
} AudioModule;
|
||||
|
||||
static const AudioModule NoopModule; ///< forward definition of noop module
|
||||
@@ -137,11 +139,12 @@ 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 volatile char AudioPaused; ///< audio paused
|
||||
static volatile char AudioVideoIsReady; ///< video ready start early
|
||||
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 = 350; ///< audio buffer time in ms
|
||||
static int AudioBufferTime = 336; ///< audio buffer time in ms
|
||||
|
||||
#ifdef USE_AUDIO_THREAD
|
||||
static pthread_t AudioThread; ///< audio play thread
|
||||
@@ -151,7 +154,7 @@ static pthread_cond_t AudioStartCond; ///< condition variable
|
||||
static const int AudioThread; ///< dummy audio thread
|
||||
#endif
|
||||
|
||||
extern int VideoAudioDelay; /// import audio/video delay
|
||||
extern int VideoAudioDelay; ///< import audio/video delay
|
||||
|
||||
#ifdef USE_AUDIORING
|
||||
|
||||
@@ -220,7 +223,7 @@ static void AudioRingInit(void)
|
||||
|
||||
for (i = 0; i < AUDIO_RING_MAX; ++i) {
|
||||
// FIXME:
|
||||
//AlsaRingBuffer = RingBufferNew(48000 * 8 * 2); // ~1s 8ch 16bit
|
||||
//AlsaRingBuffer = RingBufferNew(2 * 48000 * 8 * 2); // ~2s 8ch 16bit
|
||||
}
|
||||
// one slot always reservered
|
||||
AudioRingWrite = 1;
|
||||
@@ -289,15 +292,19 @@ static int AlsaAddToRingbuffer(const void *samples, int count)
|
||||
// too many bytes are lost
|
||||
// FIXME: should skip more, longer skip, but less often?
|
||||
}
|
||||
// Update audio clock (stupid gcc developers thinks INT64_C is unsigned)
|
||||
if (AudioPTS != (int64_t) INT64_C(0x8000000000000000)) {
|
||||
AudioPTS +=
|
||||
((int64_t) count * 90000) / (AudioSampleRate * AudioChannels *
|
||||
AudioBytesProSample);
|
||||
}
|
||||
|
||||
if (!AudioRunning) {
|
||||
if (AlsaStartThreshold < RingBufferUsedBytes(AlsaRingBuffer)) {
|
||||
Debug(4, "audio/alsa: start %4zdms\n",
|
||||
(RingBufferUsedBytes(AlsaRingBuffer) * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
|
||||
|
||||
// forced start
|
||||
if (AlsaStartThreshold * 2 < RingBufferUsedBytes(AlsaRingBuffer)) {
|
||||
return 1;
|
||||
}
|
||||
// enough video + audio buffered
|
||||
if (AudioVideoIsReady
|
||||
&& AlsaStartThreshold < RingBufferUsedBytes(AlsaRingBuffer)) {
|
||||
// restart play-back
|
||||
return 1;
|
||||
}
|
||||
@@ -326,7 +333,8 @@ static int AlsaPlayRingbuffer(void)
|
||||
if (n == -EAGAIN) {
|
||||
continue;
|
||||
}
|
||||
Error(_("audio/alsa: underrun error?\n"));
|
||||
Error(_("audio/alsa: avail underrun error? '%s'\n"),
|
||||
snd_strerror(n));
|
||||
err = snd_pcm_recover(AlsaPCMHandle, n, 0);
|
||||
if (err >= 0) {
|
||||
continue;
|
||||
@@ -342,6 +350,15 @@ static int AlsaPlayRingbuffer(void)
|
||||
if (AudioThread) {
|
||||
if (!AudioAlsaDriverBroken) {
|
||||
Error(_("audio/alsa: broken driver %d\n"), avail);
|
||||
Error("audio/alsa: state %s\n",
|
||||
snd_pcm_state_name(snd_pcm_state(AlsaPCMHandle)));
|
||||
}
|
||||
if (snd_pcm_state(AlsaPCMHandle)
|
||||
== SND_PCM_STATE_PREPARED) {
|
||||
if ((err = snd_pcm_start(AlsaPCMHandle)) < 0) {
|
||||
Error(_("audio/alsa: snd_pcm_start(): %s\n"),
|
||||
snd_strerror(err));
|
||||
}
|
||||
}
|
||||
usleep(5 * 1000);
|
||||
}
|
||||
@@ -353,6 +370,9 @@ static int AlsaPlayRingbuffer(void)
|
||||
n = RingBufferGetReadPointer(AlsaRingBuffer, &p);
|
||||
if (!n) { // ring buffer empty
|
||||
if (first) { // only error on first loop
|
||||
Debug(4, "audio/alsa: empty buffers %d\n", avail);
|
||||
// ring buffer empty
|
||||
// AlsaLowWaterMark = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -382,7 +402,8 @@ static int AlsaPlayRingbuffer(void)
|
||||
goto again;
|
||||
}
|
||||
*/
|
||||
Error(_("audio/alsa: underrun error?\n"));
|
||||
Error(_("audio/alsa: writei underrun error? '%s'\n"),
|
||||
snd_strerror(err));
|
||||
err = snd_pcm_recover(AlsaPCMHandle, err, 0);
|
||||
if (err >= 0) {
|
||||
goto again;
|
||||
@@ -411,11 +432,26 @@ static void AlsaFlushBuffers(void)
|
||||
snd_pcm_state_t state;
|
||||
|
||||
if (AlsaRingBuffer && AlsaPCMHandle) {
|
||||
#ifdef DEBUG
|
||||
const void *r;
|
||||
void *w;
|
||||
#endif
|
||||
|
||||
RingBufferReadAdvance(AlsaRingBuffer,
|
||||
RingBufferUsedBytes(AlsaRingBuffer));
|
||||
#ifdef DEBUG
|
||||
RingBufferGetWritePointer(AlsaRingBuffer, &w);
|
||||
RingBufferGetReadPointer(AlsaRingBuffer, &r);
|
||||
if (r != w) {
|
||||
Fatal(_("audio/alsa: ringbuffer out of sync %zd-%zd\n"),
|
||||
RingBufferGetWritePointer(AlsaRingBuffer, &w),
|
||||
RingBufferGetReadPointer(AlsaRingBuffer, &r));
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
state = snd_pcm_state(AlsaPCMHandle);
|
||||
Debug(3, "audio/alsa: state %d - %s\n", state,
|
||||
snd_pcm_state_name(state));
|
||||
Debug(3, "audio/alsa: flush state %s\n", snd_pcm_state_name(state));
|
||||
if (state != SND_PCM_STATE_OPEN) {
|
||||
if ((err = snd_pcm_drop(AlsaPCMHandle)) < 0) {
|
||||
Error(_("audio: snd_pcm_drop(): %s\n"), snd_strerror(err));
|
||||
@@ -427,6 +463,7 @@ static void AlsaFlushBuffers(void)
|
||||
}
|
||||
}
|
||||
AudioRunning = 0;
|
||||
AudioVideoIsReady = 0;
|
||||
AudioPTS = INT64_C(0x8000000000000000);
|
||||
}
|
||||
|
||||
@@ -451,6 +488,14 @@ static int AlsaFreeBytes(void)
|
||||
return AlsaRingBuffer ? RingBufferFreeBytes(AlsaRingBuffer) : INT32_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
** Get used bytes in audio output.
|
||||
*/
|
||||
static int AlsaUsedBytes(void)
|
||||
{
|
||||
return AlsaRingBuffer ? RingBufferUsedBytes(AlsaRingBuffer) : 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -573,8 +618,6 @@ static void AlsaEnqueue(const void *samples, int count)
|
||||
Debug(3, "audio/alsa: unpaused\n");
|
||||
}
|
||||
}
|
||||
// Update audio clock
|
||||
// AudioPTS += (size * 90000) / (AudioSampleRate * AudioChannels * AudioBytesProSample);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -633,17 +676,19 @@ static void AlsaThread(void)
|
||||
break;
|
||||
}
|
||||
// wait for space in kernel buffers
|
||||
if ((err = snd_pcm_wait(AlsaPCMHandle, 100)) < 0) {
|
||||
Error(_("audio/alsa: wait underrun error?\n"));
|
||||
if ((err = snd_pcm_wait(AlsaPCMHandle, 24)) < 0) {
|
||||
Error(_("audio/alsa: wait underrun error? '%s'\n"),
|
||||
snd_strerror(err));
|
||||
err = snd_pcm_recover(AlsaPCMHandle, err, 0);
|
||||
if (err >= 0) {
|
||||
continue;
|
||||
}
|
||||
Error(_("audio/alsa: snd_pcm_wait(): %s\n"), snd_strerror(err));
|
||||
usleep(100 * 1000);
|
||||
usleep(24 * 1000);
|
||||
continue;
|
||||
}
|
||||
if (AlsaFlushBuffer || AudioPaused) {
|
||||
// timeout or some commands
|
||||
if (!err || AlsaFlushBuffer || AudioPaused) {
|
||||
continue;
|
||||
}
|
||||
if ((err = AlsaPlayRingbuffer())) { // empty / error
|
||||
@@ -654,11 +699,12 @@ static void AlsaThread(void)
|
||||
}
|
||||
state = snd_pcm_state(AlsaPCMHandle);
|
||||
if (state != SND_PCM_STATE_RUNNING) {
|
||||
Debug(3, "audio/alsa: stopping play\n");
|
||||
Debug(3, "audio/alsa: stopping play '%s'\n",
|
||||
snd_pcm_state_name(state));
|
||||
break;
|
||||
}
|
||||
pthread_yield();
|
||||
usleep(20 * 1000); // let fill/empty the buffers
|
||||
usleep(24 * 1000); // let fill/empty the buffers
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -671,7 +717,7 @@ static void AlsaThread(void)
|
||||
*/
|
||||
static void AlsaThreadEnqueue(const void *samples, int count)
|
||||
{
|
||||
if (!AlsaRingBuffer || !AlsaPCMHandle || !AudioSampleRate) {
|
||||
if (!AlsaRingBuffer || !AlsaPCMHandle) {
|
||||
Debug(3, "audio/alsa: enqueue not ready\n");
|
||||
return;
|
||||
}
|
||||
@@ -687,6 +733,38 @@ static void AlsaThreadEnqueue(const void *samples, int count)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Video is ready, start audio if possible,
|
||||
*/
|
||||
static void AlsaVideoReady(void)
|
||||
{
|
||||
if (!AudioRunning) {
|
||||
size_t used;
|
||||
|
||||
used = RingBufferUsedBytes(AlsaRingBuffer);
|
||||
// enough video + audio buffered
|
||||
if (AlsaStartThreshold < used) {
|
||||
// too much audio buffered, skip it
|
||||
if (AlsaStartThreshold * 2 < used) {
|
||||
Debug(3, "audio/alsa: start %4zdms skip ready\n",
|
||||
((used - AlsaStartThreshold * 2) * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
|
||||
RingBufferReadAdvance(AlsaRingBuffer,
|
||||
used - AlsaStartThreshold * 2);
|
||||
}
|
||||
AudioRunning = 1;
|
||||
pthread_cond_signal(&AudioStartCond);
|
||||
}
|
||||
}
|
||||
|
||||
if (AudioSampleRate && AudioChannels) {
|
||||
Debug(3, "audio/alsa: start %4zdms video ready\n",
|
||||
(RingBufferUsedBytes(AlsaRingBuffer) * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
** Flush alsa buffers with thread.
|
||||
*/
|
||||
@@ -720,12 +798,12 @@ static snd_pcm_t *AlsaOpenPCM(int use_ac3)
|
||||
|
||||
// &&|| hell
|
||||
if (!(use_ac3 && ((device = AudioAC3Device)
|
||||
|| (device = getenv("ALSA_AC3_DEVICE"))
|
||||
|| (device = getenv("ALSA_PASSTHROUGH_DEVICE"))))
|
||||
|| (device = getenv("ALSA_AC3_DEVICE"))))
|
||||
&& !(device = AudioPCMDevice) && !(device = getenv("ALSA_DEVICE"))) {
|
||||
device = "default";
|
||||
}
|
||||
Debug(3, "audio/alsa: &&|| hell '%s'\n", device);
|
||||
Info(_("audio/alsa: using %sdevice '%s'\n"), use_ac3 ? "ac3 " : "",
|
||||
device);
|
||||
|
||||
// open none blocking; if device is already used, we don't want wait
|
||||
if ((err =
|
||||
@@ -752,7 +830,8 @@ static void AlsaInitPCM(void)
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
int err;
|
||||
snd_pcm_uframes_t buffer_size;
|
||||
|
||||
//snd_pcm_uframes_t buffer_size;
|
||||
|
||||
if (!(handle = AlsaOpenPCM(0))) {
|
||||
return;
|
||||
@@ -767,8 +846,9 @@ static void AlsaInitPCM(void)
|
||||
}
|
||||
AlsaCanPause = snd_pcm_hw_params_can_pause(hw_params);
|
||||
Info(_("audio/alsa: supports pause: %s\n"), AlsaCanPause ? "yes" : "no");
|
||||
snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size);
|
||||
Info(_("audio/alsa: max buffer size %lu\n"), buffer_size);
|
||||
// needs audio setup
|
||||
//snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size);
|
||||
//Info(_("audio/alsa: max buffer size %lu\n"), buffer_size);
|
||||
|
||||
AlsaPCMHandle = handle;
|
||||
}
|
||||
@@ -859,14 +939,17 @@ static void AlsaInitMixer(void)
|
||||
**
|
||||
** @todo FIXME: handle the case no audio running
|
||||
*/
|
||||
static uint64_t AlsaGetDelay(void)
|
||||
static int64_t AlsaGetDelay(void)
|
||||
{
|
||||
int err;
|
||||
snd_pcm_sframes_t delay;
|
||||
uint64_t pts;
|
||||
int64_t pts;
|
||||
|
||||
if (!AlsaPCMHandle || !AudioSampleRate) {
|
||||
return 0UL;
|
||||
return 0L;
|
||||
}
|
||||
if (!AudioRunning) { // audio not running
|
||||
return 0L;
|
||||
}
|
||||
// FIXME: thread safe? __assert_fail_base in snd_pcm_delay
|
||||
|
||||
@@ -884,10 +967,10 @@ static uint64_t AlsaGetDelay(void)
|
||||
delay = 0L;
|
||||
}
|
||||
|
||||
pts = ((uint64_t) delay * 90 * 1000) / AudioSampleRate;
|
||||
pts += ((uint64_t) RingBufferUsedBytes(AlsaRingBuffer) * 90 * 1000)
|
||||
pts = ((int64_t) delay * 90 * 1000) / AudioSampleRate;
|
||||
pts += ((int64_t) RingBufferUsedBytes(AlsaRingBuffer) * 90 * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample);
|
||||
Debug(4, "audio/alsa: hw+sw delay %zd %" PRId64 " ms\n",
|
||||
Debug(4, "audio/alsa: hw+sw delay %zd %" PRId64 "ms\n",
|
||||
RingBufferUsedBytes(AlsaRingBuffer), pts / 90);
|
||||
|
||||
return pts;
|
||||
@@ -921,6 +1004,9 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
|
||||
#if 1 // easy alsa hw setup way
|
||||
// flush any buffered data
|
||||
AudioFlushBuffers();
|
||||
Debug(3, "audio: %dms flush\n", (AudioUsedBytes() * 1000)
|
||||
/ (!AudioSampleRate + !AudioChannels +
|
||||
AudioSampleRate * AudioChannels * AudioBytesProSample));
|
||||
|
||||
if (1) { // close+open to fix hdmi no sound bugs
|
||||
handle = AlsaPCMHandle;
|
||||
@@ -941,7 +1027,7 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
|
||||
snd_pcm_set_params(AlsaPCMHandle, SND_PCM_FORMAT_S16,
|
||||
AlsaUseMmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED :
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED, *channels, *freq, 1,
|
||||
125 * 1000))) {
|
||||
96 * 1000))) {
|
||||
Error(_("audio/alsa: set params error: %s\n"), snd_strerror(err));
|
||||
|
||||
/*
|
||||
@@ -1091,16 +1177,21 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
|
||||
// update buffer
|
||||
|
||||
snd_pcm_get_params(AlsaPCMHandle, &buffer_size, &period_size);
|
||||
Info(_("audio/alsa: buffer size %lu, period size %lu\n"), buffer_size,
|
||||
period_size);
|
||||
Debug(3, "audio/alsa: buffer size %lu %zdms, period size %lu %zdms\n",
|
||||
buffer_size, snd_pcm_frames_to_bytes(AlsaPCMHandle,
|
||||
buffer_size) * 1000 / (AudioSampleRate * AudioChannels *
|
||||
AudioBytesProSample), period_size,
|
||||
snd_pcm_frames_to_bytes(AlsaPCMHandle,
|
||||
period_size) * 1000 / (AudioSampleRate * AudioChannels *
|
||||
AudioBytesProSample));
|
||||
Debug(3, "audio/alsa: state %s\n",
|
||||
snd_pcm_state_name(snd_pcm_state(AlsaPCMHandle)));
|
||||
|
||||
AlsaStartThreshold = snd_pcm_frames_to_bytes(AlsaPCMHandle, period_size);
|
||||
// buffer time/delay in ms
|
||||
delay = AudioBufferTime;
|
||||
if (VideoAudioDelay > -100) {
|
||||
delay += 100 + VideoAudioDelay / 90;
|
||||
if (VideoAudioDelay > 0) {
|
||||
delay += VideoAudioDelay / 90;
|
||||
}
|
||||
if (AlsaStartThreshold <
|
||||
(*freq * *channels * AudioBytesProSample * delay) / 1000U) {
|
||||
@@ -1111,7 +1202,7 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
|
||||
if (AlsaStartThreshold > RingBufferFreeBytes(AlsaRingBuffer)) {
|
||||
AlsaStartThreshold = RingBufferFreeBytes(AlsaRingBuffer);
|
||||
}
|
||||
Info(_("audio/alsa: delay %u ms\n"), (AlsaStartThreshold * 1000)
|
||||
Info(_("audio/alsa: delay %ums\n"), (AlsaStartThreshold * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
|
||||
|
||||
return ret;
|
||||
@@ -1181,7 +1272,7 @@ static void AlsaInit(void)
|
||||
#else
|
||||
(void)AlsaNoopCallback;
|
||||
#endif
|
||||
AlsaRingBuffer = RingBufferNew(48000 * 8 * 2); // ~1s 8ch 16bit
|
||||
AlsaRingBuffer = RingBufferNew(2 * 48000 * 8 * 2); // ~2s 8ch 16bit
|
||||
|
||||
AlsaInitPCM();
|
||||
AlsaInitMixer();
|
||||
@@ -1216,13 +1307,16 @@ static const AudioModule AlsaModule = {
|
||||
#ifdef USE_AUDIO_THREAD
|
||||
.Thread = AlsaThread,
|
||||
.Enqueue = AlsaThreadEnqueue,
|
||||
.VideoReady = AlsaVideoReady,
|
||||
.FlushBuffers = AlsaThreadFlushBuffers,
|
||||
#else
|
||||
.Enqueue = AlsaEnqueue,
|
||||
.VideoReady = AlsaVideoReady,
|
||||
.FlushBuffers = AlsaFlushBuffers,
|
||||
#endif
|
||||
.Poller = AlsaPoller,
|
||||
.FreeBytes = AlsaFreeBytes,
|
||||
.UsedBytes = AlsaUsedBytes,
|
||||
.GetDelay = AlsaGetDelay,
|
||||
.SetVolume = AlsaSetVolume,
|
||||
.Setup = AlsaSetup,
|
||||
@@ -1248,6 +1342,7 @@ static int OssPcmFildes = -1; ///< pcm file descriptor
|
||||
static int OssMixerFildes = -1; ///< mixer file descriptor
|
||||
static int OssMixerChannel; ///< mixer channel index
|
||||
static RingBuffer *OssRingBuffer; ///< audio ring buffer
|
||||
static int OssFragmentTime; ///< fragment time in ms
|
||||
static unsigned OssStartThreshold; ///< start play, if filled
|
||||
|
||||
#ifdef USE_AUDIO_THREAD
|
||||
@@ -1276,15 +1371,19 @@ static int OssAddToRingbuffer(const void *samples, int count)
|
||||
// too many bytes are lost
|
||||
// FIXME: should skip more, longer skip, but less often?
|
||||
}
|
||||
// Update audio clock (stupid gcc developers thinks INT64_C is unsigned)
|
||||
if (AudioPTS != (int64_t) INT64_C(0x8000000000000000)) {
|
||||
AudioPTS +=
|
||||
((int64_t) count * 90000) / (AudioSampleRate * AudioChannels *
|
||||
AudioBytesProSample);
|
||||
}
|
||||
|
||||
if (!AudioRunning) {
|
||||
if (OssStartThreshold < RingBufferUsedBytes(OssRingBuffer)) {
|
||||
Debug(4, "audio/oss: start %4zdms\n",
|
||||
(RingBufferUsedBytes(OssRingBuffer) * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
|
||||
|
||||
// forced start
|
||||
if (OssStartThreshold * 2 < RingBufferUsedBytes(OssRingBuffer)) {
|
||||
return 1;
|
||||
}
|
||||
// enough video + audio buffered
|
||||
if (AudioVideoIsReady
|
||||
&& OssStartThreshold < RingBufferUsedBytes(OssRingBuffer)) {
|
||||
// restart play-back
|
||||
return 1;
|
||||
}
|
||||
@@ -1333,7 +1432,7 @@ static int OssPlayRingbuffer(void)
|
||||
Error(_("audio/oss: write error: %s\n"), strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
Error(_("audio/oss: error not all bytes written\n"));
|
||||
Warning(_("audio/oss: error not all bytes written\n"));
|
||||
}
|
||||
// advance how many could written
|
||||
RingBufferReadAdvance(OssRingBuffer, n);
|
||||
@@ -1358,6 +1457,7 @@ static void OssFlushBuffers(void)
|
||||
}
|
||||
}
|
||||
AudioRunning = 0;
|
||||
AudioVideoIsReady = 0;
|
||||
AudioPTS = INT64_C(0x8000000000000000);
|
||||
}
|
||||
|
||||
@@ -1380,7 +1480,7 @@ static void OssEnqueue(const void *samples, int count)
|
||||
uint32_t tick;
|
||||
|
||||
tick = GetMsTicks();
|
||||
Debug(4, "audio/oss: %4d %d ms\n", count, tick - last_tick);
|
||||
Debug(4, "audio/oss: %4d %dms\n", count, tick - last_tick);
|
||||
last_tick = tick;
|
||||
#endif
|
||||
|
||||
@@ -1416,6 +1516,14 @@ static int OssFreeBytes(void)
|
||||
return OssRingBuffer ? RingBufferFreeBytes(OssRingBuffer) : INT32_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
** Get used bytes in audio output.
|
||||
*/
|
||||
static int OssUsedBytes(void)
|
||||
{
|
||||
return OssRingBuffer ? RingBufferUsedBytes(OssRingBuffer) : 0;
|
||||
}
|
||||
|
||||
#ifdef USE_AUDIO_THREAD
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -1446,10 +1554,10 @@ static void OssThread(void)
|
||||
fds[0].fd = OssPcmFildes;
|
||||
fds[0].events = POLLOUT | POLLERR;
|
||||
// wait for space in kernel buffers
|
||||
err = poll(fds, 1, 100);
|
||||
err = poll(fds, 1, OssFragmentTime);
|
||||
if (err < 0) {
|
||||
Error(_("audio/oss: error poll %s\n"), strerror(errno));
|
||||
usleep(100 * 1000);
|
||||
usleep(OssFragmentTime * 1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1462,7 +1570,7 @@ static void OssThread(void)
|
||||
break;
|
||||
}
|
||||
pthread_yield();
|
||||
usleep(20 * 1000); // let fill/empty the buffers
|
||||
usleep(OssFragmentTime * 1000); // let fill/empty the buffers
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1475,7 +1583,7 @@ static void OssThread(void)
|
||||
*/
|
||||
static void OssThreadEnqueue(const void *samples, int count)
|
||||
{
|
||||
if (!OssRingBuffer || OssPcmFildes == -1 || !AudioSampleRate) {
|
||||
if (!OssRingBuffer || OssPcmFildes == -1) {
|
||||
Debug(3, "audio/oss: enqueue not ready\n");
|
||||
return;
|
||||
}
|
||||
@@ -1486,6 +1594,26 @@ static void OssThreadEnqueue(const void *samples, int count)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Video is ready, start audio if possible,
|
||||
*/
|
||||
static void OssVideoReady(void)
|
||||
{
|
||||
if (AudioSampleRate && AudioChannels) {
|
||||
Debug(3, "audio/oss: start %4zdms video start\n",
|
||||
(RingBufferUsedBytes(OssRingBuffer) * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
|
||||
}
|
||||
|
||||
if (!AudioRunning) {
|
||||
// enough video + audio buffered
|
||||
if (OssStartThreshold < RingBufferUsedBytes(OssRingBuffer)) {
|
||||
AudioRunning = 1;
|
||||
pthread_cond_signal(&AudioStartCond);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Flush OSS buffers with thread.
|
||||
*/
|
||||
@@ -1522,7 +1650,7 @@ static int OssOpenPCM(int use_ac3)
|
||||
&& !(device = AudioPCMDevice) && !(device = getenv("OSS_AUDIODEV"))) {
|
||||
device = "/dev/dsp";
|
||||
}
|
||||
Debug(3, "audio/oss: &&|| hell '%s'\n", device);
|
||||
Info(_("audio/oss: using %sdevice '%s'\n"), use_ac3 ? "ac3" : "", device);
|
||||
|
||||
if ((fildes = open(device, O_WRONLY)) < 0) {
|
||||
Error(_("audio/oss: can't open dsp device '%s': %s\n"), device,
|
||||
@@ -1634,17 +1762,16 @@ static void OssInitMixer(void)
|
||||
**
|
||||
** @returns audio delay in time stamps.
|
||||
*/
|
||||
static uint64_t OssGetDelay(void)
|
||||
static int64_t OssGetDelay(void)
|
||||
{
|
||||
int delay;
|
||||
uint64_t pts;
|
||||
int64_t pts;
|
||||
|
||||
if (OssPcmFildes == -1) { // setup failure
|
||||
return 0UL;
|
||||
return 0L;
|
||||
}
|
||||
|
||||
if (!AudioRunning) {
|
||||
return 0UL;
|
||||
if (!AudioRunning) { // audio not running
|
||||
return 0L;
|
||||
}
|
||||
// delay in bytes in kernel buffers
|
||||
delay = -1;
|
||||
@@ -1653,18 +1780,14 @@ static uint64_t OssGetDelay(void)
|
||||
strerror(errno));
|
||||
return 0UL;
|
||||
}
|
||||
if (delay == -1) {
|
||||
delay = 0UL;
|
||||
if (delay < 0) {
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
pts = ((uint64_t) delay * 90 * 1000)
|
||||
pts = ((int64_t) (delay + RingBufferUsedBytes(OssRingBuffer)) * 90 * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample);
|
||||
pts += ((uint64_t) RingBufferUsedBytes(OssRingBuffer) * 90 * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample);
|
||||
if (pts > 600 * 90) {
|
||||
Debug(4, "audio/oss: hw+sw delay %zd %" PRId64 " ms\n",
|
||||
RingBufferUsedBytes(OssRingBuffer), pts / 90);
|
||||
}
|
||||
Debug(4, "audio/oss: hw+sw delay %zd %" PRId64 "ms\n",
|
||||
RingBufferUsedBytes(OssRingBuffer), pts / 90);
|
||||
|
||||
return pts;
|
||||
}
|
||||
@@ -1687,6 +1810,7 @@ static int OssSetup(int *freq, int *channels, int use_ac3)
|
||||
int ret;
|
||||
int tmp;
|
||||
int delay;
|
||||
audio_buf_info bi;
|
||||
|
||||
if (OssPcmFildes == -1) { // OSS not ready
|
||||
return -1;
|
||||
@@ -1750,46 +1874,54 @@ static int OssSetup(int *freq, int *channels, int use_ac3)
|
||||
|
||||
// FIXME: setup buffers
|
||||
|
||||
if (1) {
|
||||
audio_buf_info bi;
|
||||
|
||||
if (ioctl(OssPcmFildes, SNDCTL_DSP_GETOSPACE, &bi) == -1) {
|
||||
Error(_("audio/oss: ioctl(SNDCTL_DSP_GETOSPACE): %s\n"),
|
||||
strerror(errno));
|
||||
} else {
|
||||
Debug(3, "audio/oss: %d bytes buffered\n", bi.bytes);
|
||||
}
|
||||
|
||||
tmp = -1;
|
||||
if (ioctl(OssPcmFildes, SNDCTL_DSP_GETODELAY, &tmp) == -1) {
|
||||
Error(_("audio/oss: ioctl(SNDCTL_DSP_GETODELAY): %s\n"),
|
||||
strerror(errno));
|
||||
// FIXME: stop player, set setup failed flag
|
||||
return -1;
|
||||
}
|
||||
if (tmp == -1) {
|
||||
tmp = 0;
|
||||
}
|
||||
// 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 * delay) / 1000U) {
|
||||
OssStartThreshold =
|
||||
(*freq * *channels * AudioBytesProSample * delay) / 1000U;
|
||||
}
|
||||
// no bigger, than the buffer
|
||||
if (OssStartThreshold > RingBufferFreeBytes(OssRingBuffer)) {
|
||||
OssStartThreshold = RingBufferFreeBytes(OssRingBuffer);
|
||||
}
|
||||
|
||||
Info(_("audio/oss: delay %u ms\n"), (OssStartThreshold * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
|
||||
#ifdef SNDCTL_DSP_POLICY
|
||||
tmp = 3;
|
||||
if (ioctl(OssPcmFildes, SNDCTL_DSP_POLICY, &tmp) == -1) {
|
||||
Error(_("audio/oss: ioctl(SNDCTL_DSP_POLICY): %s\n"), strerror(errno));
|
||||
} else {
|
||||
Info("audio/oss: set policy to %d\n", tmp);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ioctl(OssPcmFildes, SNDCTL_DSP_GETOSPACE, &bi) == -1) {
|
||||
Error(_("audio/oss: ioctl(SNDCTL_DSP_GETOSPACE): %s\n"),
|
||||
strerror(errno));
|
||||
bi.fragsize = 4096;
|
||||
bi.fragstotal = 16;
|
||||
} else {
|
||||
Debug(3, "audio/oss: %d bytes buffered\n", bi.bytes);
|
||||
}
|
||||
|
||||
OssFragmentTime = (bi.fragsize * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample);
|
||||
|
||||
Debug(3, "audio/oss: buffer size %d %dms, fragment size %d %dms\n",
|
||||
bi.fragsize * bi.fragstotal, (bi.fragsize * bi.fragstotal * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample), bi.fragsize,
|
||||
OssFragmentTime);
|
||||
|
||||
// start when enough bytes for initial write
|
||||
OssStartThreshold = (bi.fragsize - 1) * bi.fragstotal;
|
||||
|
||||
// buffer time/delay in ms
|
||||
delay = AudioBufferTime + 300;
|
||||
if (VideoAudioDelay > 0) {
|
||||
delay += VideoAudioDelay / 90;
|
||||
}
|
||||
if (OssStartThreshold <
|
||||
(AudioSampleRate * AudioChannels * AudioBytesProSample * delay) /
|
||||
1000U) {
|
||||
OssStartThreshold =
|
||||
(AudioSampleRate * AudioChannels * AudioBytesProSample * delay) /
|
||||
1000U;
|
||||
}
|
||||
// no bigger, than the buffer
|
||||
if (OssStartThreshold > RingBufferFreeBytes(OssRingBuffer)) {
|
||||
OssStartThreshold = RingBufferFreeBytes(OssRingBuffer);
|
||||
}
|
||||
|
||||
Info(_("audio/oss: delay %ums\n"), (OssStartThreshold * 1000)
|
||||
/ (AudioSampleRate * AudioChannels * AudioBytesProSample));
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1813,7 +1945,7 @@ void OssPause(void)
|
||||
*/
|
||||
static void OssInit(void)
|
||||
{
|
||||
OssRingBuffer = RingBufferNew(48000 * 8 * 2); // ~1s 8ch 16bit
|
||||
OssRingBuffer = RingBufferNew(2 * 48000 * 8 * 2); // ~2s 8ch 16bit
|
||||
|
||||
OssInitPCM();
|
||||
OssInitMixer();
|
||||
@@ -1843,13 +1975,16 @@ static const AudioModule OssModule = {
|
||||
#ifdef USE_AUDIO_THREAD
|
||||
.Thread = OssThread,
|
||||
.Enqueue = OssThreadEnqueue,
|
||||
.VideoReady = OssVideoReady,
|
||||
.FlushBuffers = OssThreadFlushBuffers,
|
||||
#else
|
||||
.Enqueue = OssEnqueue,
|
||||
.VideoReady = OssVideoReady,
|
||||
.FlushBuffers = OssFlushBuffers,
|
||||
#endif
|
||||
.Poller = OssPoller,
|
||||
.FreeBytes = OssFreeBytes,
|
||||
.UsedBytes = OssUsedBytes,
|
||||
.GetDelay = OssGetDelay,
|
||||
.SetVolume = OssSetVolume,
|
||||
.Setup = OssSetup,
|
||||
@@ -1885,14 +2020,22 @@ static int NoopFreeBytes(void)
|
||||
return INT32_MAX; // no driver, much space
|
||||
}
|
||||
|
||||
/**
|
||||
** Get used bytes in audio output.
|
||||
*/
|
||||
static int NoopUsedBytes(void)
|
||||
{
|
||||
return 0; // no driver, nothing used
|
||||
}
|
||||
|
||||
/**
|
||||
** Get audio delay in time stamps.
|
||||
**
|
||||
** @returns audio delay in time stamps.
|
||||
*/
|
||||
static uint64_t NoopGetDelay(void)
|
||||
static int64_t NoopGetDelay(void)
|
||||
{
|
||||
return 0UL;
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1932,9 +2075,11 @@ static void NoopVoid(void)
|
||||
static const AudioModule NoopModule = {
|
||||
.Name = "noop",
|
||||
.Enqueue = NoopEnqueue,
|
||||
.VideoReady = NoopVoid,
|
||||
.FlushBuffers = NoopVoid,
|
||||
.Poller = NoopVoid,
|
||||
.FreeBytes = NoopFreeBytes,
|
||||
.UsedBytes = NoopUsedBytes,
|
||||
.GetDelay = NoopGetDelay,
|
||||
.SetVolume = NoopSetVolume,
|
||||
.Setup = NoopSetup,
|
||||
@@ -1964,6 +2109,11 @@ static void *AudioPlayHandlerThread(void *dummy)
|
||||
pthread_cond_wait(&AudioStartCond, &AudioMutex);
|
||||
// cond_wait can return, without signal!
|
||||
} while (!AudioRunning);
|
||||
|
||||
Debug(3, "audio: ----> %dms start\n", (AudioUsedBytes() * 1000)
|
||||
/ (!AudioSampleRate + !AudioChannels +
|
||||
AudioSampleRate * AudioChannels * AudioBytesProSample));
|
||||
|
||||
pthread_mutex_unlock(&AudioMutex);
|
||||
|
||||
#ifdef USE_AUDIORING
|
||||
@@ -1997,7 +2147,6 @@ static void *AudioPlayHandlerThread(void *dummy)
|
||||
}
|
||||
#endif
|
||||
|
||||
Debug(3, "audio: play start\n");
|
||||
AudioUsedModule->Thread();
|
||||
}
|
||||
|
||||
@@ -2064,7 +2213,41 @@ static const AudioModule *AudioModules[] = {
|
||||
*/
|
||||
void AudioEnqueue(const void *samples, int count)
|
||||
{
|
||||
if (!AudioSampleRate || !AudioChannels) {
|
||||
return; // not setup
|
||||
}
|
||||
if (0) {
|
||||
static uint32_t last;
|
||||
static uint32_t tick;
|
||||
static uint32_t max = 101;
|
||||
int64_t delay;
|
||||
|
||||
delay = AudioGetDelay();
|
||||
tick = GetMsTicks();
|
||||
if ((last && tick - last > max) && AudioRunning) {
|
||||
|
||||
//max = tick - last;
|
||||
Debug(3, "audio: packet delta %d %lu\n", tick - last, delay / 90);
|
||||
}
|
||||
last = tick;
|
||||
}
|
||||
AudioUsedModule->Enqueue(samples, count);
|
||||
|
||||
// Update audio clock (stupid gcc developers thinks INT64_C is unsigned)
|
||||
if (AudioPTS != (int64_t) INT64_C(0x8000000000000000)) {
|
||||
AudioPTS +=
|
||||
((int64_t) count * 90 * 1000) / (AudioSampleRate * AudioChannels *
|
||||
AudioBytesProSample);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Video is ready.
|
||||
*/
|
||||
void AudioVideoReady(void)
|
||||
{
|
||||
AudioVideoIsReady = 1;
|
||||
AudioUsedModule->VideoReady();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2091,12 +2274,20 @@ int AudioFreeBytes(void)
|
||||
return AudioUsedModule->FreeBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
** Get used bytes in audio output.
|
||||
*/
|
||||
int AudioUsedBytes(void)
|
||||
{
|
||||
return AudioUsedModule->UsedBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
** Get audio delay in time stamps.
|
||||
**
|
||||
** @returns audio delay in time stamps.
|
||||
*/
|
||||
uint64_t AudioGetDelay(void)
|
||||
int64_t AudioGetDelay(void)
|
||||
{
|
||||
return AudioUsedModule->GetDelay();
|
||||
}
|
||||
@@ -2110,9 +2301,8 @@ void AudioSetClock(int64_t pts)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (AudioPTS != pts) {
|
||||
Debug(4, "audio: set clock to %#012" PRIx64 " %#012" PRIx64 " pts\n",
|
||||
AudioPTS, pts);
|
||||
|
||||
Debug(4, "audio: set clock %s -> %s pts\n", Timestamp2String(AudioPTS),
|
||||
Timestamp2String(pts));
|
||||
}
|
||||
#endif
|
||||
AudioPTS = pts;
|
||||
@@ -2183,11 +2373,12 @@ int AudioSetup(int *freq, int *channels, int use_ac3)
|
||||
void AudioPlay(void)
|
||||
{
|
||||
if (!AudioPaused) {
|
||||
Warning("audio: not paused, check the code\n");
|
||||
Debug(3, "audio: not paused, check the code\n");
|
||||
return;
|
||||
}
|
||||
Debug(3, "audio: resumed\n");
|
||||
AudioPaused = 0;
|
||||
AudioEnqueue(NULL, 0); // wakeup thread
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2196,13 +2387,28 @@ void AudioPlay(void)
|
||||
void AudioPause(void)
|
||||
{
|
||||
if (AudioPaused) {
|
||||
Warning("audio: already paused, check the code\n");
|
||||
Debug(3, "audio: already paused, check the code\n");
|
||||
return;
|
||||
}
|
||||
Debug(3, "audio: paused\n");
|
||||
AudioPaused = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
** Set audio buffer time.
|
||||
**
|
||||
** PES audio packets have a max distance of 300 ms.
|
||||
** TS audio packet have a max distance of 100 ms.
|
||||
** The period size of the audio buffer is 24 ms.
|
||||
*/
|
||||
void AudioSetBufferTime(int delay)
|
||||
{
|
||||
if (!delay) {
|
||||
delay = 336;
|
||||
}
|
||||
AudioBufferTime = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
** Set pcm audio device.
|
||||
**
|
||||
|
||||
7
audio.h
7
audio.h
@@ -31,9 +31,8 @@ extern void AudioEnqueue(const void *, int); ///< buffer audio samples
|
||||
extern void AudioFlushBuffers(void); ///< flush audio buffers
|
||||
extern void AudioPoller(void); ///< poll audio events/handling
|
||||
extern int AudioFreeBytes(void); ///< free bytes in audio output
|
||||
|
||||
//extern int AudioUsedBytes(void); ///< used bytes in audio output
|
||||
extern uint64_t AudioGetDelay(void); ///< get current audio delay
|
||||
extern int AudioUsedBytes(void); ///< used bytes in audio output
|
||||
extern int64_t AudioGetDelay(void); ///< get current audio delay
|
||||
extern void AudioSetClock(int64_t); ///< set audio clock base
|
||||
extern int64_t AudioGetClock(); ///< get current audio clock
|
||||
extern void AudioSetVolume(int); ///< set volume
|
||||
@@ -42,6 +41,8 @@ extern int AudioSetup(int *, int *, int); ///< setup audio output
|
||||
extern void AudioPlay(void); ///< play audio
|
||||
extern void AudioPause(void); ///< pause audio
|
||||
|
||||
extern void AudioSetBufferTime(int); ///< set audio buffer time
|
||||
|
||||
extern void AudioSetDevice(const char *); ///< set PCM audio device
|
||||
extern void AudioSetDeviceAC3(const char *); ///< set pass-through device
|
||||
extern void AudioSetChannel(const char *); ///< set mixer channel
|
||||
|
||||
819
codec.c
819
codec.c
@@ -30,13 +30,10 @@
|
||||
/// many bugs and incompatiblity in it. Don't use this shit.
|
||||
///
|
||||
|
||||
/**
|
||||
** use av_parser to support insane dvb audio streams.
|
||||
*/
|
||||
#define USE_AVPARSER
|
||||
|
||||
/// compile with passthrough support (experimental)
|
||||
/// compile with passthrough support (stable, ac3 only)
|
||||
#define USE_PASSTHROUGH
|
||||
/// compile audio drift correction support (experimental)
|
||||
#define noUSE_AUDIO_DRIFT_CORRECTION
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
@@ -355,7 +352,7 @@ void CodecVideoOpen(VideoDecoder * decoder, const char *name, int codec_id)
|
||||
{
|
||||
AVCodec *video_codec;
|
||||
|
||||
Debug(3, "codec: using codec %s or ID %#04x\n", name, codec_id);
|
||||
Debug(3, "codec: using video codec %s or ID %#06x\n", name, codec_id);
|
||||
|
||||
if (decoder->VideoCtx) {
|
||||
Error(_("codec: missing close\n"));
|
||||
@@ -380,7 +377,7 @@ void CodecVideoOpen(VideoDecoder * decoder, const char *name, int codec_id)
|
||||
if (name && (video_codec = avcodec_find_decoder_by_name(name))) {
|
||||
Debug(3, "codec: vdpau decoder found\n");
|
||||
} else if (!(video_codec = avcodec_find_decoder(codec_id))) {
|
||||
Fatal(_("codec: codec ID %#04x not found\n"), codec_id);
|
||||
Fatal(_("codec: codec ID %#06x not found\n"), codec_id);
|
||||
// FIXME: none fatal
|
||||
}
|
||||
decoder->VideoCodec = video_codec;
|
||||
@@ -561,6 +558,11 @@ void CodecVideoDecode(VideoDecoder * decoder, const AVPacket * avpkt)
|
||||
video_ctx->frame_number, used);
|
||||
}
|
||||
if (used != pkt->size) {
|
||||
// ffmpeg 0.8.7 dislikes our seq_end_h264 and enters endless loop here
|
||||
if (used == 0 && pkt->size == 5 && pkt->data[4] == 0x0A) {
|
||||
Warning("codec: ffmpeg 0.8.x workaround used\n");
|
||||
return;
|
||||
}
|
||||
if (used >= 0 && used < pkt->size) {
|
||||
// some tv channels, produce this
|
||||
Debug(4,
|
||||
@@ -603,8 +605,6 @@ struct _audio_decoder_
|
||||
AVCodec *AudioCodec; ///< audio codec
|
||||
AVCodecContext *AudioCtx; ///< audio codec context
|
||||
|
||||
/// 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
|
||||
@@ -614,6 +614,21 @@ struct _audio_decoder_
|
||||
|
||||
ReSampleContext *ReSample; ///< audio resampling context
|
||||
|
||||
int64_t LastDelay; ///< last delay
|
||||
struct timespec LastTime; ///< last time
|
||||
int64_t LastPTS; ///< last PTS
|
||||
|
||||
int Drift; ///< accumulated audio drift
|
||||
int DriftCorr; ///< audio drift correction value
|
||||
int DriftFrac; ///< audio drift fraction for ac3
|
||||
|
||||
struct AVResampleContext *AvResample; ///< second audio resample context
|
||||
#define MAX_CHANNELS 8 ///< max number of channels supported
|
||||
int16_t *Buffer[MAX_CHANNELS]; ///< deinterleave sample buffers
|
||||
int BufferSize; ///< size of sample buffer
|
||||
int16_t *Remain[MAX_CHANNELS]; ///< filter remaining samples
|
||||
int RemainSize; ///< size of remain buffer
|
||||
int RemainCount; ///< number of remaining samples
|
||||
};
|
||||
|
||||
#ifdef USE_PASSTHROUGH
|
||||
@@ -626,6 +641,7 @@ static char CodecPassthroughAC3; ///< pass ac3 through
|
||||
|
||||
static const int CodecPassthroughAC3 = 0;
|
||||
#endif
|
||||
static char CodecDownmix; ///< enable ac-3 downmix
|
||||
|
||||
/**
|
||||
** Allocate a new audio decoder context.
|
||||
@@ -665,10 +681,12 @@ void CodecAudioOpen(AudioDecoder * audio_decoder, const char *name,
|
||||
{
|
||||
AVCodec *audio_codec;
|
||||
|
||||
Debug(3, "codec: using audio codec %s or ID %#06x\n", name, codec_id);
|
||||
|
||||
if (name && (audio_codec = avcodec_find_decoder_by_name(name))) {
|
||||
Debug(3, "codec: audio decoder '%s' found\n", name);
|
||||
} else if (!(audio_codec = avcodec_find_decoder(codec_id))) {
|
||||
Fatal(_("codec: codec ID %#04x not found\n"), codec_id);
|
||||
Fatal(_("codec: codec ID %#06x not found\n"), codec_id);
|
||||
// FIXME: errors aren't fatal
|
||||
}
|
||||
audio_decoder->AudioCodec = audio_codec;
|
||||
@@ -676,6 +694,12 @@ void CodecAudioOpen(AudioDecoder * audio_decoder, const char *name,
|
||||
if (!(audio_decoder->AudioCtx = avcodec_alloc_context3(audio_codec))) {
|
||||
Fatal(_("codec: can't allocate audio codec context\n"));
|
||||
}
|
||||
|
||||
if (CodecDownmix) {
|
||||
audio_decoder->AudioCtx->request_channels = 2;
|
||||
audio_decoder->AudioCtx->request_channel_layout =
|
||||
AV_CH_LAYOUT_STEREO_DOWNMIX;
|
||||
}
|
||||
pthread_mutex_lock(&CodecLockMutex);
|
||||
// open codec
|
||||
#if LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(53,5,0)
|
||||
@@ -684,9 +708,19 @@ void CodecAudioOpen(AudioDecoder * audio_decoder, const char *name,
|
||||
Fatal(_("codec: can't open audio codec\n"));
|
||||
}
|
||||
#else
|
||||
if (avcodec_open2(audio_decoder->AudioCtx, audio_codec, NULL) < 0) {
|
||||
pthread_mutex_unlock(&CodecLockMutex);
|
||||
Fatal(_("codec: can't open audio codec\n"));
|
||||
if (1) {
|
||||
AVDictionary *av_dict;
|
||||
|
||||
av_dict = NULL;
|
||||
// FIXME: import settings
|
||||
//av_dict_set(&av_dict, "dmix_mode", "0", 0);
|
||||
//av_dict_set(&av_dict, "ltrt_cmixlev", "1.414", 0);
|
||||
//av_dict_set(&av_dict, "loro_cmixlev", "1.414", 0);
|
||||
if (avcodec_open2(audio_decoder->AudioCtx, audio_codec, &av_dict) < 0) {
|
||||
pthread_mutex_unlock(&CodecLockMutex);
|
||||
Fatal(_("codec: can't open audio codec\n"));
|
||||
}
|
||||
av_dict_free(&av_dict);
|
||||
}
|
||||
#endif
|
||||
pthread_mutex_unlock(&CodecLockMutex);
|
||||
@@ -694,17 +728,14 @@ void CodecAudioOpen(AudioDecoder * audio_decoder, const char *name,
|
||||
|
||||
if (audio_codec->capabilities & CODEC_CAP_TRUNCATED) {
|
||||
Debug(3, "codec: audio can use truncated packets\n");
|
||||
// we do not send complete frames
|
||||
audio_decoder->AudioCtx->flags |= CODEC_FLAG_TRUNCATED;
|
||||
}
|
||||
if (!(audio_decoder->AudioParser =
|
||||
av_parser_init(audio_decoder->AudioCtx->codec_id))) {
|
||||
Fatal(_("codec: can't init audio parser\n"));
|
||||
// we send only complete frames
|
||||
// audio_decoder->AudioCtx->flags |= CODEC_FLAG_TRUNCATED;
|
||||
}
|
||||
audio_decoder->SampleRate = 0;
|
||||
audio_decoder->Channels = 0;
|
||||
audio_decoder->HwSampleRate = 0;
|
||||
audio_decoder->HwChannels = 0;
|
||||
audio_decoder->LastDelay = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -715,14 +746,25 @@ void CodecAudioOpen(AudioDecoder * audio_decoder, const char *name,
|
||||
void CodecAudioClose(AudioDecoder * audio_decoder)
|
||||
{
|
||||
// FIXME: output any buffered data
|
||||
if (audio_decoder->AvResample) {
|
||||
int ch;
|
||||
|
||||
av_resample_close(audio_decoder->AvResample);
|
||||
audio_decoder->AvResample = NULL;
|
||||
audio_decoder->RemainCount = 0;
|
||||
audio_decoder->BufferSize = 0;
|
||||
audio_decoder->RemainSize = 0;
|
||||
for (ch = 0; ch < MAX_CHANNELS; ++ch) {
|
||||
free(audio_decoder->Buffer[ch]);
|
||||
audio_decoder->Buffer[ch] = NULL;
|
||||
free(audio_decoder->Remain[ch]);
|
||||
audio_decoder->Remain[ch] = NULL;
|
||||
}
|
||||
}
|
||||
if (audio_decoder->ReSample) {
|
||||
audio_resample_close(audio_decoder->ReSample);
|
||||
audio_decoder->ReSample = NULL;
|
||||
}
|
||||
if (audio_decoder->AudioParser) {
|
||||
av_parser_close(audio_decoder->AudioParser);
|
||||
audio_decoder->AudioParser = NULL;
|
||||
}
|
||||
if (audio_decoder->AudioCtx) {
|
||||
pthread_mutex_lock(&CodecLockMutex);
|
||||
avcodec_close(audio_decoder->AudioCtx);
|
||||
@@ -742,12 +784,26 @@ void CodecSetAudioPassthrough(int mask)
|
||||
(void)mask;
|
||||
}
|
||||
|
||||
/**
|
||||
** Set audio downmix.
|
||||
**
|
||||
** @param onoff enable/disable downmix.
|
||||
*/
|
||||
void CodecSetAudioDownmix(int onoff)
|
||||
{
|
||||
CodecDownmix = onoff;
|
||||
}
|
||||
|
||||
/**
|
||||
** 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
|
||||
**
|
||||
** @param buf[IN,OUT] sample buffer
|
||||
** @param size size of sample buffer in bytes
|
||||
** @param channels number of channels interleaved in sample buffer
|
||||
*/
|
||||
static void CodecReorderAudioFrame(int16_t * buf, int size, int channels)
|
||||
{
|
||||
@@ -798,7 +854,277 @@ static void CodecReorderAudioFrame(int16_t * buf, int size, int channels)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_AVPARSER
|
||||
/**
|
||||
** Set/update audio pts clock.
|
||||
**
|
||||
** @param audio_decoder audio decoder data
|
||||
** @param pts presentation timestamp
|
||||
*/
|
||||
static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts)
|
||||
{
|
||||
struct timespec nowtime;
|
||||
int64_t delay;
|
||||
int64_t tim_diff;
|
||||
int64_t pts_diff;
|
||||
int drift;
|
||||
int corr;
|
||||
|
||||
AudioSetClock(pts);
|
||||
|
||||
delay = AudioGetDelay();
|
||||
if (!delay) {
|
||||
return;
|
||||
}
|
||||
clock_gettime(CLOCK_REALTIME, &nowtime);
|
||||
if (!audio_decoder->LastDelay) {
|
||||
audio_decoder->LastTime = nowtime;
|
||||
audio_decoder->LastPTS = pts;
|
||||
audio_decoder->LastDelay = delay;
|
||||
audio_decoder->Drift = 0;
|
||||
audio_decoder->DriftFrac = 0;
|
||||
Debug(3, "codec/audio: inital delay %zd ms\n", delay / 90);
|
||||
return;
|
||||
}
|
||||
// collect over some time
|
||||
pts_diff = pts - audio_decoder->LastPTS;
|
||||
if (pts_diff < 10 * 1000 * 90) {
|
||||
return;
|
||||
}
|
||||
|
||||
tim_diff = (nowtime.tv_sec - audio_decoder->LastTime.tv_sec)
|
||||
* 1000 * 1000 * 1000 + (nowtime.tv_nsec -
|
||||
audio_decoder->LastTime.tv_nsec);
|
||||
|
||||
drift =
|
||||
(tim_diff * 90) / (1000 * 1000) - pts_diff + delay -
|
||||
audio_decoder->LastDelay;
|
||||
|
||||
// adjust rounding error
|
||||
nowtime.tv_nsec -= nowtime.tv_nsec % (1000 * 1000 / 90);
|
||||
audio_decoder->LastTime = nowtime;
|
||||
audio_decoder->LastPTS = pts;
|
||||
audio_decoder->LastDelay = delay;
|
||||
|
||||
if (0) {
|
||||
Debug(3, "codec/audio: interval P:%5zdms T:%5zdms D:%4zdms %f %d\n",
|
||||
pts_diff / 90, tim_diff / (1000 * 1000), delay / 90, drift / 90.0,
|
||||
audio_decoder->DriftCorr);
|
||||
}
|
||||
// underruns and av_resample have the same time :(((
|
||||
if (abs(drift) > 10 * 90) {
|
||||
// drift too big, pts changed?
|
||||
Debug(3, "codec/audio: drift(%6d) %3dms reset\n",
|
||||
audio_decoder->DriftCorr, drift / 90);
|
||||
audio_decoder->LastDelay = 0;
|
||||
} else {
|
||||
|
||||
drift += audio_decoder->Drift;
|
||||
audio_decoder->Drift = drift;
|
||||
corr = (10 * audio_decoder->HwSampleRate * drift) / (90 * 1000);
|
||||
#if defined(USE_PASSTHROUGH) && !defined(USE_AC3_DRIFT_CORRECTION)
|
||||
// SPDIF/HDMI passthrough
|
||||
if (!CodecPassthroughAC3
|
||||
|| audio_decoder->AudioCtx->codec_id != CODEC_ID_AC3)
|
||||
#endif
|
||||
{
|
||||
audio_decoder->DriftCorr = -corr;
|
||||
}
|
||||
|
||||
if (audio_decoder->DriftCorr < -20000) { // limit correction
|
||||
audio_decoder->DriftCorr = -20000;
|
||||
} else if (audio_decoder->DriftCorr > 20000) {
|
||||
audio_decoder->DriftCorr = 20000;
|
||||
}
|
||||
}
|
||||
// FIXME: this works with libav 0.8, and only with >10ms with ffmpeg 0.10
|
||||
if (audio_decoder->AvResample && audio_decoder->DriftCorr) {
|
||||
int distance;
|
||||
|
||||
// try workaround for buggy ffmpeg 0.10
|
||||
if (abs(audio_decoder->DriftCorr) < 2000) {
|
||||
distance = (pts_diff * audio_decoder->HwSampleRate) / (900 * 1000);
|
||||
} else {
|
||||
distance = (pts_diff * audio_decoder->HwSampleRate) / (90 * 1000);
|
||||
}
|
||||
av_resample_compensate(audio_decoder->AvResample,
|
||||
audio_decoder->DriftCorr / 10, distance);
|
||||
}
|
||||
Debug(3, "codec/audio: drift(%6d) %8dus %5d\n", audio_decoder->DriftCorr,
|
||||
drift * 1000 / 90, corr);
|
||||
}
|
||||
|
||||
/**
|
||||
** Handle audio format changes.
|
||||
**
|
||||
** @param audio_decoder audio decoder data
|
||||
*/
|
||||
static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder)
|
||||
{
|
||||
const AVCodecContext *audio_ctx;
|
||||
int err;
|
||||
int isAC3;
|
||||
|
||||
// FIXME: use swr_convert from swresample (only in ffmpeg!)
|
||||
if (audio_decoder->ReSample) {
|
||||
audio_resample_close(audio_decoder->ReSample);
|
||||
audio_decoder->ReSample = NULL;
|
||||
}
|
||||
if (audio_decoder->AvResample) {
|
||||
av_resample_close(audio_decoder->AvResample);
|
||||
audio_decoder->AvResample = NULL;
|
||||
audio_decoder->RemainCount = 0;
|
||||
}
|
||||
|
||||
audio_ctx = audio_decoder->AudioCtx;
|
||||
Debug(3, "codec/audio: format change %dHz %d channels %s\n",
|
||||
audio_ctx->sample_rate, audio_ctx->channels,
|
||||
CodecPassthroughAC3 ? "pass-through" : "");
|
||||
|
||||
audio_decoder->SampleRate = audio_ctx->sample_rate;
|
||||
audio_decoder->HwSampleRate = audio_ctx->sample_rate;
|
||||
audio_decoder->Channels = audio_ctx->channels;
|
||||
audio_decoder->PassthroughAC3 = CodecPassthroughAC3;
|
||||
|
||||
// SPDIF/HDMI passthrough
|
||||
if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
|
||||
audio_decoder->HwChannels = 2;
|
||||
isAC3 = 1;
|
||||
} else {
|
||||
audio_decoder->HwChannels = audio_ctx->channels;
|
||||
isAC3 = 0;
|
||||
}
|
||||
|
||||
// channels not support?
|
||||
if ((err =
|
||||
AudioSetup(&audio_decoder->HwSampleRate,
|
||||
&audio_decoder->HwChannels, isAC3))) {
|
||||
Debug(3, "codec/audio: resample %dHz *%d -> %dHz *%d\n",
|
||||
audio_ctx->sample_rate, audio_ctx->channels,
|
||||
audio_decoder->HwSampleRate, audio_decoder->HwChannels);
|
||||
|
||||
if (err == 1) {
|
||||
audio_decoder->ReSample =
|
||||
av_audio_resample_init(audio_decoder->HwChannels,
|
||||
audio_ctx->channels, audio_decoder->HwSampleRate,
|
||||
audio_ctx->sample_rate, audio_ctx->sample_fmt,
|
||||
audio_ctx->sample_fmt, 16, 10, 0, 0.8);
|
||||
// libav-0.8_pre didn't support 6 -> 2 channels
|
||||
if (!audio_decoder->ReSample) {
|
||||
Error(_("codec/audio: resample setup error\n"));
|
||||
audio_decoder->HwChannels = 0;
|
||||
audio_decoder->HwSampleRate = 0;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Debug(3, "codec/audio: audio setup error\n");
|
||||
// FIXME: handle errors
|
||||
audio_decoder->HwChannels = 0;
|
||||
audio_decoder->HwSampleRate = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// prepare audio drift resample
|
||||
#ifdef USE_AUDIO_DRIFT_CORRECTION
|
||||
if (!isAC3) {
|
||||
if (audio_decoder->AvResample) {
|
||||
Error(_("codec/audio: overwrite resample\n"));
|
||||
}
|
||||
audio_decoder->AvResample =
|
||||
av_resample_init(audio_decoder->HwSampleRate,
|
||||
audio_decoder->HwSampleRate, 16, 10, 0, 0.8);
|
||||
if (!audio_decoder->AvResample) {
|
||||
Error(_("codec/audio: AvResample setup error\n"));
|
||||
} else {
|
||||
// reset drift to some default value
|
||||
audio_decoder->DriftCorr /= 2;
|
||||
audio_decoder->DriftFrac = 0;
|
||||
av_resample_compensate(audio_decoder->AvResample,
|
||||
audio_decoder->DriftCorr / 10,
|
||||
10 * audio_decoder->HwSampleRate);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
** Codec enqueue audio samples.
|
||||
**
|
||||
** @param audio_decoder audio decoder data
|
||||
** @param data samples data
|
||||
** @param count number of bytes in sample data
|
||||
*/
|
||||
void CodecAudioEnqueue(AudioDecoder * audio_decoder, int16_t * data, int count)
|
||||
{
|
||||
#ifdef USE_AUDIO_DRIFT_CORRECTION
|
||||
if (audio_decoder->AvResample) {
|
||||
int16_t buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4 +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE] __attribute__ ((aligned(16)));
|
||||
int16_t buftmp[MAX_CHANNELS][(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4];
|
||||
int consumed;
|
||||
int i;
|
||||
int n;
|
||||
int ch;
|
||||
int bytes_n;
|
||||
|
||||
bytes_n = count / audio_decoder->HwChannels;
|
||||
// resize sample buffer, if needed
|
||||
if (audio_decoder->RemainCount + bytes_n > audio_decoder->BufferSize) {
|
||||
audio_decoder->BufferSize = audio_decoder->RemainCount + bytes_n;
|
||||
for (ch = 0; ch < MAX_CHANNELS; ++ch) {
|
||||
audio_decoder->Buffer[ch] =
|
||||
realloc(audio_decoder->Buffer[ch],
|
||||
audio_decoder->BufferSize);
|
||||
}
|
||||
}
|
||||
// copy remaining bytes into sample buffer
|
||||
for (ch = 0; ch < audio_decoder->HwChannels; ++ch) {
|
||||
memcpy(audio_decoder->Buffer[ch], audio_decoder->Remain[ch],
|
||||
audio_decoder->RemainCount);
|
||||
}
|
||||
// deinterleave samples into sample buffer
|
||||
for (i = 0; i < bytes_n / 2; i++) {
|
||||
for (ch = 0; ch < audio_decoder->HwChannels; ++ch) {
|
||||
audio_decoder->Buffer[ch][audio_decoder->RemainCount / 2 + i]
|
||||
= data[i * audio_decoder->HwChannels + ch];
|
||||
}
|
||||
}
|
||||
|
||||
bytes_n += audio_decoder->RemainSize;
|
||||
n = 0; // keep gcc lucky
|
||||
// resample the sample buffer into tmp buffer
|
||||
for (ch = 0; ch < audio_decoder->HwChannels; ++ch) {
|
||||
n = av_resample(audio_decoder->AvResample, buftmp[ch],
|
||||
audio_decoder->Buffer[ch], &consumed, bytes_n / 2,
|
||||
sizeof(buftmp[ch]) / 2, ch == audio_decoder->HwChannels - 1);
|
||||
// fixme remaining channels
|
||||
if (bytes_n - consumed * 2 > audio_decoder->RemainSize) {
|
||||
audio_decoder->RemainSize = bytes_n - consumed * 2;
|
||||
}
|
||||
audio_decoder->Remain[ch] =
|
||||
realloc(audio_decoder->Remain[ch], audio_decoder->RemainSize);
|
||||
memcpy(audio_decoder->Remain[ch],
|
||||
audio_decoder->Buffer[ch] + consumed,
|
||||
audio_decoder->RemainSize);
|
||||
audio_decoder->RemainCount = audio_decoder->RemainSize;
|
||||
}
|
||||
|
||||
// interleave samples from sample buffer
|
||||
for (i = 0; i < n; i++) {
|
||||
for (ch = 0; ch < audio_decoder->HwChannels; ++ch) {
|
||||
buf[i * audio_decoder->HwChannels + ch] = buftmp[ch][i];
|
||||
}
|
||||
}
|
||||
n *= 2;
|
||||
|
||||
n *= audio_decoder->HwChannels;
|
||||
CodecReorderAudioFrame(buf, n, audio_decoder->HwChannels);
|
||||
AudioEnqueue(buf, n);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
CodecReorderAudioFrame(data, count, audio_decoder->HwChannels);
|
||||
AudioEnqueue(data, count);
|
||||
}
|
||||
|
||||
/**
|
||||
** Decode an audio packet.
|
||||
@@ -812,324 +1138,172 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
|
||||
{
|
||||
int16_t buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4 +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE] __attribute__ ((aligned(16)));
|
||||
int buf_sz;
|
||||
int l;
|
||||
AVCodecContext *audio_ctx;
|
||||
int index;
|
||||
|
||||
//#define spkt avpkt
|
||||
#if 1 // didn't fix crash in av_parser_parse2
|
||||
AVPacket spkt[1];
|
||||
|
||||
// av_new_packet reserves FF_INPUT_BUFFER_PADDING_SIZE and clears it
|
||||
if (av_new_packet(spkt, avpkt->size)) {
|
||||
Error(_("codec: out of memory\n"));
|
||||
return;
|
||||
}
|
||||
memcpy(spkt->data, avpkt->data, avpkt->size);
|
||||
spkt->pts = avpkt->pts;
|
||||
spkt->dts = avpkt->dts;
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
if (!audio_decoder->AudioParser) {
|
||||
Fatal(_("codec: internal error parser freeded while running\n"));
|
||||
}
|
||||
#endif
|
||||
|
||||
audio_ctx = audio_decoder->AudioCtx;
|
||||
index = 0;
|
||||
while (spkt->size > index) {
|
||||
int n;
|
||||
int l;
|
||||
AVPacket dpkt[1];
|
||||
|
||||
av_init_packet(dpkt);
|
||||
n = av_parser_parse2(audio_decoder->AudioParser, audio_ctx,
|
||||
&dpkt->data, &dpkt->size, spkt->data + index, spkt->size - index,
|
||||
!index ? (uint64_t) spkt->pts : AV_NOPTS_VALUE,
|
||||
!index ? (uint64_t) spkt->dts : AV_NOPTS_VALUE, -1);
|
||||
|
||||
// FIXME: make this a function for both #ifdef cases
|
||||
if (dpkt->size) {
|
||||
int buf_sz;
|
||||
|
||||
dpkt->pts = audio_decoder->AudioParser->pts;
|
||||
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;
|
||||
}
|
||||
buf_sz = sizeof(buf);
|
||||
l = avcodec_decode_audio3(audio_ctx, buf, &buf_sz, (AVPacket *) avpkt);
|
||||
if (avpkt->size != l) {
|
||||
if (l == AVERROR(EAGAIN)) {
|
||||
Error(_("codec: latm\n"));
|
||||
return;
|
||||
}
|
||||
if (l < 0) { // no audio frame could be decompressed
|
||||
Error(_("codec: error audio data\n"));
|
||||
return;
|
||||
}
|
||||
Error(_("codec: error more than one frame data\n"));
|
||||
}
|
||||
#ifdef notyetFF_API_OLD_DECODE_AUDIO
|
||||
// FIXME: ffmpeg git comeing
|
||||
int got_frame;
|
||||
// FIXME: ffmpeg git comeing
|
||||
int got_frame;
|
||||
|
||||
avcodec_decode_audio4(audio_ctx, frame, &got_frame, dpkt);
|
||||
avcodec_decode_audio4(audio_ctx, frame, &got_frame, avpkt);
|
||||
#else
|
||||
#endif
|
||||
// Update audio clock
|
||||
if ((uint64_t) dpkt->pts != AV_NOPTS_VALUE) {
|
||||
AudioSetClock(dpkt->pts);
|
||||
}
|
||||
// FIXME: must first play remainings bytes, than change and play new.
|
||||
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;
|
||||
}
|
||||
// update audio clock
|
||||
if (avpkt->pts != (int64_t) AV_NOPTS_VALUE) {
|
||||
CodecAudioSetClock(audio_decoder, avpkt->pts);
|
||||
}
|
||||
// FIXME: must first play remainings bytes, than change and play new.
|
||||
if (audio_decoder->PassthroughAC3 != CodecPassthroughAC3
|
||||
|| audio_decoder->SampleRate != audio_ctx->sample_rate
|
||||
|| audio_decoder->Channels != audio_ctx->channels) {
|
||||
CodecAudioUpdateFormat(audio_decoder);
|
||||
}
|
||||
|
||||
audio_decoder->SampleRate = audio_ctx->sample_rate;
|
||||
audio_decoder->HwSampleRate = audio_ctx->sample_rate;
|
||||
audio_decoder->Channels = audio_ctx->channels;
|
||||
// SPDIF/HDMI passthrough
|
||||
if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
|
||||
audio_decoder->HwChannels = 2;
|
||||
isAC3 = 1;
|
||||
} else {
|
||||
audio_decoder->HwChannels = audio_ctx->channels;
|
||||
isAC3 = 0;
|
||||
}
|
||||
if (audio_decoder->HwSampleRate && audio_decoder->HwChannels) {
|
||||
// need to resample audio
|
||||
if (audio_decoder->ReSample) {
|
||||
int16_t outbuf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4 +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE]
|
||||
__attribute__ ((aligned(16)));
|
||||
int outlen;
|
||||
|
||||
// channels not support?
|
||||
if ((err =
|
||||
AudioSetup(&audio_decoder->HwSampleRate,
|
||||
&audio_decoder->HwChannels, isAC3))) {
|
||||
Debug(3, "codec/audio: resample %dHz *%d -> %dHz *%d\n",
|
||||
audio_ctx->sample_rate, audio_ctx->channels,
|
||||
audio_decoder->HwSampleRate,
|
||||
audio_decoder->HwChannels);
|
||||
|
||||
if (err == 1) {
|
||||
audio_decoder->ReSample =
|
||||
av_audio_resample_init(audio_decoder->HwChannels,
|
||||
audio_ctx->channels, audio_decoder->HwSampleRate,
|
||||
audio_ctx->sample_rate, audio_ctx->sample_fmt,
|
||||
audio_ctx->sample_fmt, 16, 10, 0, 0.8);
|
||||
// libav-0.8_pre didn't support 6 -> 2 channels
|
||||
if (!audio_decoder->ReSample) {
|
||||
Error(_("codec/audio: resample setup error\n"));
|
||||
audio_decoder->HwChannels = 0;
|
||||
audio_decoder->HwSampleRate = 0;
|
||||
}
|
||||
} else {
|
||||
Debug(3, "codec/audio: audio setup error\n");
|
||||
// FIXME: handle errors
|
||||
audio_decoder->HwChannels = 0;
|
||||
audio_decoder->HwSampleRate = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_decoder->HwSampleRate && audio_decoder->HwChannels) {
|
||||
// need to resample audio
|
||||
if (audio_decoder->ReSample) {
|
||||
int16_t outbuf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4 +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE]
|
||||
__attribute__ ((aligned(16)));
|
||||
int outlen;
|
||||
|
||||
// FIXME: libav-0.7.2 crash here
|
||||
outlen =
|
||||
audio_resample(audio_decoder->ReSample, outbuf, buf,
|
||||
buf_sz);
|
||||
// FIXME: libav-0.7.2 crash here
|
||||
outlen =
|
||||
audio_resample(audio_decoder->ReSample, outbuf, buf, buf_sz);
|
||||
#ifdef DEBUG
|
||||
if (outlen != buf_sz) {
|
||||
Debug(3, "codec/audio: possible fixed ffmpeg\n");
|
||||
}
|
||||
if (outlen != buf_sz) {
|
||||
Debug(3, "codec/audio: possible fixed ffmpeg\n");
|
||||
}
|
||||
#endif
|
||||
if (outlen) {
|
||||
// outlen seems to be wrong in ffmpeg-0.9
|
||||
outlen /= audio_decoder->Channels *
|
||||
av_get_bytes_per_sample(audio_ctx->sample_fmt);
|
||||
outlen *=
|
||||
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 {
|
||||
if (outlen) {
|
||||
// outlen seems to be wrong in ffmpeg-0.9
|
||||
outlen /= audio_decoder->Channels *
|
||||
av_get_bytes_per_sample(audio_ctx->sample_fmt);
|
||||
outlen *=
|
||||
audio_decoder->HwChannels *
|
||||
av_get_bytes_per_sample(audio_ctx->sample_fmt);
|
||||
Debug(4, "codec/audio: %d -> %d\n", buf_sz, outlen);
|
||||
CodecAudioEnqueue(audio_decoder, outbuf, outlen);
|
||||
}
|
||||
} else {
|
||||
#ifdef USE_PASSTHROUGH
|
||||
// SPDIF/HDMI passthrough
|
||||
if (CodecPassthroughAC3
|
||||
&& audio_ctx->codec_id == CODEC_ID_AC3) {
|
||||
// build SPDIF header and append A52 audio to it
|
||||
// dpkt is the original data
|
||||
buf_sz = 6144;
|
||||
if (buf_sz < dpkt->size + 8) {
|
||||
Error(_
|
||||
("codec/audio: decoded data smaller than encoded\n"));
|
||||
break;
|
||||
}
|
||||
// copy original data for output
|
||||
// FIXME: not 100% sure, if endian is correct
|
||||
buf[0] = htole16(0xF872); // iec 61937 sync word
|
||||
buf[1] = htole16(0x4E1F);
|
||||
buf[2] = htole16(0x01 | (dpkt->data[5] & 0x07) << 8);
|
||||
buf[3] = htole16(dpkt->size * 8);
|
||||
swab(dpkt->data, buf + 4, dpkt->size);
|
||||
memset(buf + 4 + dpkt->size / 2, 0,
|
||||
buf_sz - 8 - dpkt->size);
|
||||
// SPDIF/HDMI passthrough
|
||||
if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
|
||||
// build SPDIF header and append A52 audio to it
|
||||
// avpkt is the original data
|
||||
buf_sz = 6144;
|
||||
|
||||
#ifdef USE_AC3_DRIFT_CORRECTION
|
||||
if (1) {
|
||||
int x;
|
||||
|
||||
x = (audio_decoder->DriftFrac +
|
||||
(audio_decoder->DriftCorr * buf_sz)) / (10 *
|
||||
audio_decoder->HwSampleRate * 100);
|
||||
audio_decoder->DriftFrac =
|
||||
(audio_decoder->DriftFrac +
|
||||
(audio_decoder->DriftCorr * buf_sz)) % (10 *
|
||||
audio_decoder->HwSampleRate * 100);
|
||||
x *= audio_decoder->HwChannels * 4;
|
||||
if (x < -64) { // limit correction
|
||||
x = -64;
|
||||
} else if (x > 64) {
|
||||
x = 64;
|
||||
}
|
||||
#if 0
|
||||
//
|
||||
// old experimental code
|
||||
//
|
||||
if (1) {
|
||||
// FIXME: need to detect dts
|
||||
// copy original data for output
|
||||
// FIXME: buf is sint
|
||||
buf[0] = 0x72;
|
||||
buf[1] = 0xF8;
|
||||
buf[2] = 0x1F;
|
||||
buf[3] = 0x4E;
|
||||
buf[4] = 0x00;
|
||||
switch (dpkt->size) {
|
||||
case 512:
|
||||
buf[5] = 0x0B;
|
||||
break;
|
||||
case 1024:
|
||||
buf[5] = 0x0C;
|
||||
break;
|
||||
case 2048:
|
||||
buf[5] = 0x0D;
|
||||
break;
|
||||
default:
|
||||
Debug(3,
|
||||
"codec/audio: dts sample burst not supported\n");
|
||||
buf[5] = 0x00;
|
||||
break;
|
||||
}
|
||||
buf[6] = (dpkt->size * 8);
|
||||
buf[7] = (dpkt->size * 8) >> 8;
|
||||
//buf[8] = 0x0B;
|
||||
//buf[9] = 0x77;
|
||||
//printf("%x %x\n", dpkt->data[0],dpkt->data[1]);
|
||||
// swab?
|
||||
memcpy(buf + 8, dpkt->data, dpkt->size);
|
||||
memset(buf + 8 + dpkt->size, 0,
|
||||
buf_sz - 8 - dpkt->size);
|
||||
} else if (1) {
|
||||
// FIXME: need to detect mp2
|
||||
// FIXME: mp2 passthrough
|
||||
// see softhddev.c version/layer
|
||||
// 0x04 mpeg1 layer1
|
||||
// 0x05 mpeg1 layer23
|
||||
// 0x06 mpeg2 ext
|
||||
// 0x07 mpeg2.5 layer 1
|
||||
// 0x08 mpeg2.5 layer 2
|
||||
// 0x09 mpeg2.5 layer 3
|
||||
}
|
||||
// DTS HD?
|
||||
// True HD?
|
||||
#endif
|
||||
#endif
|
||||
CodecReorderAudioFrame(buf, buf_sz,
|
||||
audio_decoder->HwChannels);
|
||||
AudioEnqueue(buf, buf_sz);
|
||||
buf_sz += x;
|
||||
}
|
||||
#endif
|
||||
if (buf_sz < avpkt->size + 8) {
|
||||
Error(_
|
||||
("codec/audio: decoded data smaller than encoded\n"));
|
||||
return;
|
||||
}
|
||||
// copy original data for output
|
||||
// FIXME: not 100% sure, if endian is correct
|
||||
buf[0] = htole16(0xF872); // iec 61937 sync word
|
||||
buf[1] = htole16(0x4E1F);
|
||||
buf[2] = htole16(0x01 | (avpkt->data[5] & 0x07) << 8);
|
||||
buf[3] = htole16(avpkt->size * 8);
|
||||
swab(avpkt->data, buf + 4, avpkt->size);
|
||||
memset(buf + 4 + avpkt->size / 2, 0, buf_sz - 8 - avpkt->size);
|
||||
// don't play with the ac-3 samples
|
||||
AudioEnqueue(buf, buf_sz);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dpkt->size > l) {
|
||||
Error(_("codec: error more than one frame data\n"));
|
||||
#if 0
|
||||
//
|
||||
// old experimental code
|
||||
//
|
||||
if (1) {
|
||||
// FIXME: need to detect dts
|
||||
// copy original data for output
|
||||
// FIXME: buf is sint
|
||||
buf[0] = 0x72;
|
||||
buf[1] = 0xF8;
|
||||
buf[2] = 0x1F;
|
||||
buf[3] = 0x4E;
|
||||
buf[4] = 0x00;
|
||||
switch (avpkt->size) {
|
||||
case 512:
|
||||
buf[5] = 0x0B;
|
||||
break;
|
||||
case 1024:
|
||||
buf[5] = 0x0C;
|
||||
break;
|
||||
case 2048:
|
||||
buf[5] = 0x0D;
|
||||
break;
|
||||
default:
|
||||
Debug(3,
|
||||
"codec/audio: dts sample burst not supported\n");
|
||||
buf[5] = 0x00;
|
||||
break;
|
||||
}
|
||||
buf[6] = (avpkt->size * 8);
|
||||
buf[7] = (avpkt->size * 8) >> 8;
|
||||
//buf[8] = 0x0B;
|
||||
//buf[9] = 0x77;
|
||||
//printf("%x %x\n", avpkt->data[0],avpkt->data[1]);
|
||||
// swab?
|
||||
memcpy(buf + 8, avpkt->data, avpkt->size);
|
||||
memset(buf + 8 + avpkt->size, 0, buf_sz - 8 - avpkt->size);
|
||||
} else if (1) {
|
||||
// FIXME: need to detect mp2
|
||||
// FIXME: mp2 passthrough
|
||||
// see softhddev.c version/layer
|
||||
// 0x04 mpeg1 layer1
|
||||
// 0x05 mpeg1 layer23
|
||||
// 0x06 mpeg2 ext
|
||||
// 0x07 mpeg2.5 layer 1
|
||||
// 0x08 mpeg2.5 layer 2
|
||||
// 0x09 mpeg2.5 layer 3
|
||||
}
|
||||
}
|
||||
|
||||
index += n;
|
||||
}
|
||||
|
||||
#if 1
|
||||
// or av_free_packet, make no difference here
|
||||
av_destruct_packet(spkt);
|
||||
// DTS HD?
|
||||
// True HD?
|
||||
#endif
|
||||
#endif
|
||||
CodecAudioEnqueue(audio_decoder, buf, buf_sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
** Decode an audio packet.
|
||||
**
|
||||
** PTS must be handled self.
|
||||
**
|
||||
** @param audio_decoder audio decoder data
|
||||
** @param avpkt audio packet
|
||||
*/
|
||||
void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
|
||||
{
|
||||
int16_t buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4 +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE] __attribute__ ((aligned(16)));
|
||||
AVCodecContext *audio_ctx;
|
||||
int index;
|
||||
|
||||
//#define spkt avpkt
|
||||
#if 1
|
||||
AVPacket spkt[1];
|
||||
|
||||
// av_new_packet reserves FF_INPUT_BUFFER_PADDING_SIZE and clears it
|
||||
if (av_new_packet(spkt, avpkt->size)) {
|
||||
Error(_("codec: out of memory\n"));
|
||||
return;
|
||||
}
|
||||
memcpy(spkt->data, avpkt->data, avpkt->size);
|
||||
spkt->pts = avpkt->pts;
|
||||
spkt->dts = avpkt->dts;
|
||||
#endif
|
||||
audio_ctx = audio_decoder->AudioCtx;
|
||||
index = 0;
|
||||
while (spkt->size > index) {
|
||||
int n;
|
||||
int buf_sz;
|
||||
AVPacket dpkt[1];
|
||||
|
||||
av_init_packet(dpkt);
|
||||
dpkt->data = spkt->data + index;
|
||||
dpkt->size = spkt->size - index;
|
||||
|
||||
buf_sz = sizeof(buf);
|
||||
n = avcodec_decode_audio3(audio_ctx, buf, &buf_sz, dpkt);
|
||||
if (n < 0) { // no audio frame could be decompressed
|
||||
Error(_("codec: error audio data at %d\n"), index);
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
Debug(4, "codec/audio: -> %d\n", buf_sz);
|
||||
if ((unsigned)buf_sz > sizeof(buf)) {
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
#ifdef notyetFF_API_OLD_DECODE_AUDIO
|
||||
// FIXME: ffmpeg git comeing
|
||||
int got_frame;
|
||||
|
||||
avcodec_decode_audio4(audio_ctx, frame, &got_frame, dpkt);
|
||||
#else
|
||||
#endif
|
||||
// FIXME: see above, old code removed
|
||||
|
||||
index += n;
|
||||
}
|
||||
|
||||
#if 1
|
||||
// or av_free_packet, make no difference here
|
||||
av_destruct_packet(spkt);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
** Flush the audio decoder.
|
||||
**
|
||||
@@ -1137,7 +1311,6 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
|
||||
*/
|
||||
void CodecAudioFlushBuffers(AudioDecoder * decoder)
|
||||
{
|
||||
// FIXME: reset audio parser
|
||||
avcodec_flush_buffers(decoder->AudioCtx);
|
||||
}
|
||||
|
||||
|
||||
3
codec.h
3
codec.h
@@ -67,6 +67,9 @@ extern void CodecAudioOpen(AudioDecoder *, const char *, int);
|
||||
/// Close audio codec.
|
||||
extern void CodecAudioClose(AudioDecoder *);
|
||||
|
||||
/// Decode an audio packet.
|
||||
extern void CodecAudioDecodeOld(AudioDecoder *, const AVPacket *);
|
||||
|
||||
/// Decode an audio packet.
|
||||
extern void CodecAudioDecode(AudioDecoder *, const AVPacket *);
|
||||
|
||||
|
||||
25
misc.h
25
misc.h
@@ -107,6 +107,31 @@ static inline void Syslog(const int level, const char *format, ...)
|
||||
#define Debug(level, fmt...) /* disabled */
|
||||
#endif
|
||||
|
||||
#ifndef AV_NOPTS_VALUE
|
||||
#define AV_NOPTS_VALUE INT64_C(0x8000000000000000)
|
||||
#endif
|
||||
|
||||
/**
|
||||
** Nice time-stamp string.
|
||||
**
|
||||
** @param ts dvb time stamp
|
||||
*/
|
||||
static inline const char *Timestamp2String(int64_t ts)
|
||||
{
|
||||
static char buf[4][16];
|
||||
static int idx;
|
||||
|
||||
if (ts == (int64_t) AV_NOPTS_VALUE) {
|
||||
return "--:--:--.---";
|
||||
}
|
||||
idx = (idx + 1) % 3;
|
||||
snprintf(buf[idx], sizeof(buf[idx]), "%2d:%02d:%02d.%03d",
|
||||
(int)(ts / (90 * 3600000)), (int)((ts / (90 * 60000)) % 60),
|
||||
(int)((ts / (90 * 1000)) % 60), (int)((ts / 90) % 1000));
|
||||
|
||||
return buf[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
** Get ticks in ms.
|
||||
**
|
||||
|
||||
1752
softhddev.c
1752
softhddev.c
File diff suppressed because it is too large
Load Diff
16
softhddev.h
16
softhddev.h
@@ -37,8 +37,8 @@ extern "C"
|
||||
|
||||
/// C plugin play audio packet
|
||||
extern int PlayAudio(const uint8_t *, int, uint8_t);
|
||||
/// C plugin mute audio
|
||||
extern void Mute(void);
|
||||
/// C plugin play TS audio packet
|
||||
extern int PlayTsAudio(const uint8_t *, int);
|
||||
/// C plugin set audio volume
|
||||
extern void SetVolumeDevice(int);
|
||||
|
||||
@@ -50,13 +50,19 @@ extern "C"
|
||||
extern uint8_t *GrabImage(int *, int, int, int, int);
|
||||
|
||||
/// C plugin set play mode
|
||||
extern void SetPlayMode(void);
|
||||
extern int SetPlayMode(int);
|
||||
/// C plugin get current system time counter
|
||||
extern int64_t GetSTC(void);
|
||||
/// C plugin set trick speed
|
||||
extern void TrickSpeed(int);
|
||||
/// C plugin clears all video and audio data from the device
|
||||
extern void Clear(void);
|
||||
/// C plugin sets the device into play mode
|
||||
extern void Play(void);
|
||||
/// C plugin sets the device into "freeze frame" mode
|
||||
extern void Freeze(void);
|
||||
/// C plugin mute audio
|
||||
extern void Mute(void);
|
||||
/// C plugin display I-frame as a still picture.
|
||||
extern void StillPicture(const uint8_t *, int);
|
||||
/// C plugin poll if ready
|
||||
@@ -72,9 +78,11 @@ extern "C"
|
||||
/// C plugin exit + cleanup
|
||||
extern void SoftHdDeviceExit(void);
|
||||
/// C plugin start code
|
||||
extern void Start(void);
|
||||
extern int Start(void);
|
||||
/// C plugin stop code
|
||||
extern void Stop(void);
|
||||
/// C plugin house keeping
|
||||
extern void Housekeeping(void);
|
||||
/// C plugin main thread hook
|
||||
extern void MainThreadHook(void);
|
||||
|
||||
|
||||
725
softhddevice.cpp
725
softhddevice.cpp
File diff suppressed because it is too large
Load Diff
36
video.h
36
video.h
@@ -30,6 +30,13 @@
|
||||
/// Video hardware decoder typedef
|
||||
typedef struct _video_hw_decoder_ VideoHwDecoder;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Variables
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
extern char VideoIgnoreRepeatPict; ///< disable repeat pict warning
|
||||
extern int VideoAudioDelay; ///< audio/video delay
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Prototypes
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -71,9 +78,18 @@ extern void VideoPollEvent(void);
|
||||
/// Wakeup display handler.
|
||||
extern void VideoDisplayWakeup(void);
|
||||
|
||||
/// Set video device.
|
||||
extern void VideoSetDevice(const char *);
|
||||
|
||||
/// Set video geometry.
|
||||
extern int VideoSetGeometry(const char *);
|
||||
|
||||
/// Set 60Hz display mode.
|
||||
extern void VideoSet60HzMode(int);
|
||||
|
||||
/// Set soft start audio/video sync.
|
||||
extern void VideoSetSoftStartSync(int);
|
||||
|
||||
/// Set video output position.
|
||||
extern void VideoSetOutputPosition(int, int, int, int);
|
||||
|
||||
@@ -104,12 +120,18 @@ extern void VideoSetDenoise(int[]);
|
||||
/// Set sharpen.
|
||||
extern void VideoSetSharpen(int[]);
|
||||
|
||||
/// Set skip lines.
|
||||
extern void VideoSetSkipLines(int);
|
||||
/// Set cut top and bottom.
|
||||
extern void VideoSetCutTopBottom(int[]);
|
||||
|
||||
/// Set cut left and right.
|
||||
extern void VideoSetCutLeftRight(int[]);
|
||||
|
||||
/// Set studio levels.
|
||||
extern void VideoSetStudioLevels(int);
|
||||
|
||||
/// Set background.
|
||||
extern void VideoSetBackground(uint32_t);
|
||||
|
||||
/// Set audio delay.
|
||||
extern void VideoSetAudioDelay(int);
|
||||
|
||||
@@ -125,7 +147,14 @@ extern void VideoOsdDrawARGB(int, int, int, int, const uint8_t *);
|
||||
/// Get OSD size.
|
||||
extern void VideoGetOsdSize(int *, int *);
|
||||
|
||||
extern int64_t VideoGetClock(void); ///< Get video clock.
|
||||
/// Set video clock.
|
||||
extern void VideoSetClock(VideoHwDecoder *, int64_t);
|
||||
|
||||
/// Get video clock.
|
||||
extern int64_t VideoGetClock(const VideoHwDecoder *);
|
||||
|
||||
/// Set trick play speed.
|
||||
extern void VideoSetTrickSpeed(VideoHwDecoder *, int);
|
||||
|
||||
/// Grab screen.
|
||||
extern uint8_t *VideoGrab(int *, int *, int *, int);
|
||||
@@ -138,5 +167,6 @@ extern void VideoExit(void); ///< Cleanup and exit video module.
|
||||
|
||||
extern void VideoFlushInput(void); ///< Flush video input buffers.
|
||||
extern int VideoDecode(void); ///< Decode video input buffers.
|
||||
extern int VideoGetBuffers(void); ///< Get number of input buffers.
|
||||
|
||||
/// @}
|
||||
|
||||
Reference in New Issue
Block a user