88 Commits
0.4.8 ... 0.5.0

Author SHA1 Message Date
Johns
e10e62dcf7 Release Version 0.5.0. 2012-04-07 20:21:55 +02:00
Johns
2a1793c98e Use config value to change audio/video delay. 2012-04-07 13:43:51 +02:00
Johns
30d4586448 Change audio/video delay with hot-key. 2012-04-07 00:15:57 +02:00
Johns
aa4debc9c8 Enable/disable/toggle fullscreen with hot-key. 2012-04-06 14:38:51 +02:00
ac2e10a308 Cutting pixels configured for each resolution. 2012-04-05 22:51:20 +02:00
Johns
c986d285ea Buffer less video and audio. 2012-04-05 15:47:59 +02:00
Johns
8612044b9b Move suspend on inactivity to houesekeeping. 2012-04-05 15:43:32 +02:00
Johns
c19b86411a Update documents. 2012-04-05 15:42:48 +02:00
Johns
9165052d5e Fix gcc error bug (also for VA-API). 2012-04-03 16:36:06 +02:00
Johns
413983a666 Calling TrickSpeed without decoder can happen. 2012-04-01 17:04:16 +02:00
Johns
f86fa4edd7 VDR 1.7.27 suggested change. 2012-04-01 17:02:15 +02:00
Johns
7f8110557f Fix gcc error bug. 2012-03-31 23:20:06 +02:00
Johns
c9b344a3fd Audio/Video sync rewrite.
Trick-speed support moved to video module.
Reduce video messages.
2012-03-31 21:27:54 +02:00
Johns
b41f934c37 Faster VdpauBlackSurface version. 2012-03-30 17:19:31 +02:00
Johns
6058f3da56 Fix bug: VideoSetPts wrong position. 2012-03-30 16:04:25 +02:00
689d75b808 Add VideoSkipPixels support. 2012-03-26 20:49:18 +02:00
Johns
bd4503f30b More debug for flush buffers. Bigger audio buffer. 2012-03-23 18:43:20 +01:00
Johns
24ba8175a3 Disable suspend on inactivity until player fixed. 2012-03-22 16:06:32 +01:00
Johns
fe24cbb182 mp3 needs 100% cpu again! 2012-03-20 16:36:42 +01:00
Johns
6eff8fa818 Forgot VDPAU in requires. 2012-03-19 17:15:21 +01:00
Johns
552a994db3 Add optional argument to ATTA svdrp commmand. 2012-03-15 15:42:51 +01:00
Johns
d24f19bc2d More SVDRP commands help. 2012-03-14 15:07:08 +01:00
Johns
7b570c507c Cleanups. 2012-03-12 17:58:19 +01:00
Johns
09ba3e2993 Let inactivity suspend wakeup with remote keys. 2012-03-11 14:12:49 +01:00
Johns
d0f825f831 Comments added. 2012-03-10 17:46:00 +01:00
Johns
47d2896468 Better Poll(), flush video buffers after replay. 2012-03-10 17:05:41 +01:00
Johns
f59425ac57 AudioGetDelay returns signed value and cleanups. 2012-03-10 15:00:58 +01:00
Johns
1acdeee913 Adds ffmpeg 0.8.7 bug workaround:
Single nal end seq aren't consumed and an endless loop entered.
2012-03-09 21:47:06 +01:00
Johns
c2938c7ef3 Wakeup display to show OSD for remote learning. 2012-03-09 12:08:56 +01:00
Johns
d65fe88c83 Support switching the primary device with svdrp. 2012-03-08 15:28:10 +01:00
Johns
7d3f4f4434 Disable and reenable screen saver and DPMS. 2012-03-08 15:25:10 +01:00
Johns
acc35fe30c Video cleanup.
Add noop video output module.
Move VideoThread check into lock/unlock functions.
Add support for choosing video output module.
2012-03-07 15:31:43 +01:00
Johns
ee5804fed7 Handle snd_pcm_wait timeouts. 2012-03-07 15:13:07 +01:00
Johns
1cbaddf75c Need extra space in ring buffer for sequence end. 2012-03-06 18:37:40 +01:00
Johns
226760490b VADisplayAttribDirectSurface removed. 2012-03-06 16:56:26 +01:00
Johns
7931909e28 Workaround should be for abs. 2012-03-06 15:39:29 +01:00
Johns
129c139ed7 Fix fast backward with some h264 streams. 2012-03-06 15:38:30 +01:00
Johns
340816d763 Make soft start sync setup menu configurable. 2012-03-06 12:16:47 +01:00
Johns
d6c6818ecf Workaround for av_resample_compensate ffmpeg bug.
FFmpeg commit a67cb012e6947fb238193afc0f18114f6e20818c or
1b9ca38d9d06d319fffd61d27e4eb385d6572ba8 breaks av_resample_compensate.
Only big sample_delta compensation_distance ratios are now working.
2012-03-05 20:38:43 +01:00
Johns
181a0bb372 Move grab unsupported warning to low-level. 2012-03-05 20:10:23 +01:00
Johns
f2d4163899 Fix bug: NAL end of sequence is 10 and not 0x10.
Cleanup, remove old cruft.
Add support for pes recordings.
2012-03-05 20:05:56 +01:00
Johns
4cc98d7937 Move time-stamp printing to misc.h. 2012-03-05 17:34:10 +01:00
Johns
3812fa8d38 Fix bug: AudioEnqueue crash without sound card. 2012-03-05 15:06:46 +01:00
Johns
da5c5cd5fd Version 0.4.9 released. 2012-03-04 22:36:14 +01:00
Johns
74a62e3649 Makes audio ts parser default. Suspend fixes. 2012-03-03 18:47:07 +01:00
Johns
7e1a42f7ed Experimental ac3 audio drift correction support. 2012-03-03 16:45:59 +01:00
Johns
dda9011abc Removes LPCM detection from TS parser. 2012-03-03 16:11:38 +01:00
Johns
de79e9211f Disabled audio drift correction as default. 2012-03-02 18:17:51 +01:00
Johns
b0d9f41020 Rewrote video/audio start code. 2012-03-02 18:16:20 +01:00
Johns
4d1a516c80 Fix Bug: PES audio buffer not correct reset. 2012-03-02 16:06:45 +01:00
Johns
995f1286bd Fix attach. 2012-03-02 00:38:52 +01:00
Johns
fd0ae12f24 Fix warning. 2012-03-02 00:28:53 +01:00
Johns
db258a0fbd Detach/Attach on MakePrimaryDevice. 2012-03-02 00:22:08 +01:00
Johns
0df8e8a5fc Handle initial suspend mode like normal suspend. 2012-03-02 00:05:03 +01:00
Johns
6a28064dce Add support for attach/detach plugin. 2012-03-01 22:12:22 +01:00
Johns
b5e9077c74 Increase AudioBufferTime for OSS. 2012-03-01 17:50:57 +01:00
Johns
3b4ace14cf Add ac3 to info message for pass-through. 2012-02-29 18:21:28 +01:00
Johns
5aa868c296 Don't change correction value during pass-through. 2012-02-29 17:40:58 +01:00
Johns
43b48224b5 Improved audio drift correction support. 2012-02-29 16:35:49 +01:00
Johns
144f22314f Experimental audio drift correction support. 2012-02-27 23:13:53 +01:00
Johns
51eb720265 VideoSetFullscreen needs X11 connection. 2012-02-26 20:54:31 +01:00
Johns
e977007dd3 Removed old cruft. 2012-02-26 14:30:46 +01:00
Johns
769f00b4f6 Try to restart alsa after underrun. 2012-02-25 18:10:19 +01:00
Johns
aa426cd8b2 Add SVDRP HOTK command support and cleanup. 2012-02-25 13:02:15 +01:00
Johns
b2cab00599 Remove AVDictionary. 2012-02-24 18:16:24 +01:00
Johns
b54d62ef35 Video background color documentation. 2012-02-24 15:42:32 +01:00
Johns
9b68248a3e Increased audio buffer time for PES packets. 2012-02-24 15:41:17 +01:00
Johns
762959fbb4 Only a single frame is supported. 2012-02-24 15:38:04 +01:00
Johns
07b426f2b5 Fix bug in new audio ts parser: hangup. 2012-02-24 15:22:26 +01:00
Johns
668a6ec277 Support configuration and set of video background. 2012-02-24 15:15:50 +01:00
Johns
82f61de117 Include GIT version, when build from git. 2012-02-23 23:33:00 +01:00
Johns
67e571f02b Survive lost X11 display. 2012-02-23 17:57:21 +01:00
Johns
c17af0e958 Fix bug: 100% cpu use with plugins like mp3. 2012-02-23 15:32:43 +01:00
Johns
2561214c3e Info time should be 1 minute and not ~1 second. 2012-02-22 18:37:50 +01:00
Johns
7382bd60ff VA-API branch staging support. 2012-02-22 16:50:35 +01:00
Johns
73b93f1aba Makes A/V sync info time configurable. 2012-02-22 16:32:40 +01:00
Johns
0243b1c8a7 Fix bug: No OSD until valid video stream shown. 2012-02-22 15:10:47 +01:00
Johns
6ce760ccd8 60Hz display mode configurable with setup.conf. 2012-02-22 15:06:05 +01:00
Johns
2f869884ba Support downmix of AC-3 to stero. 2012-02-21 22:36:10 +01:00
Johns
5d8dea1b6b New audio PES handling.
New easier and more flexible audio PES packet parser, which includes own
codec parser.
Removed av_parser use.
Reduced audio buffer time, faster channel switch.
New audio transport stream parser (not enabled as default).
2012-02-21 20:55:28 +01:00
Johns
1f232db5b4 Nicer debug output when clock out of range. 2012-02-19 22:45:29 +01:00
Johns
c4ad13c53f Fix bug: Grabbing JPG image fails while suspended. 2012-02-19 20:52:57 +01:00
Johns
98f73f2199 Add support for hot keys. 2012-02-19 19:22:03 +01:00
Johns
89ca44206c Add support to use characters input in edit mode. 2012-02-17 16:37:38 +01:00
Johns
5c9b85b69b Use SetVideoFormat to call SetVideoDisplayFormat. 2012-02-17 15:10:24 +01:00
Johns
09cfab3856 Add posibility to disable repeat pict warning. 2012-02-16 21:55:14 +01:00
Johns
30e903d90a Wakeup audio thread after pause. 2012-02-16 18:41:46 +01:00
Johns
852d367225 Adds trick speed support. 2012-02-16 15:31:53 +01:00
14 changed files with 4260 additions and 1768 deletions

View File

@@ -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

View File

@@ -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` \

View File

@@ -20,23 +20,24 @@ $Id$
A software and GPU emulated HD output device plugin for VDR.
o Video VA-API/VA-API (with intel, nvidia and amd backend supported)
o Video CPU/VA-API
o Video VDPAU/VDPAU
o Video CPU/VDPAU
o Audio FFMpeg/Alsa/Analog
o Audio FFMpeg/Alsa/Digital
o Audio FFMpeg/OSS/Analog
o HDMI/SPDIF Passthrough
o VA-API bob software deinterlace
o Auto-crop
o Video decoder CPU / VA-API / VDPAU
o Video output VA-API / VDPAU
o Audio FFMpeg / Alsa / Analog
o Audio FFMpeg / Alsa / Digital
o Audio FFMpeg / OSS / Analog
o HDMI/SPDIF pass-through
o YaepgHD support
o Software deinterlacer Bob (VA-API only)
o Autocrop
o Grab image (VDPAU only)
o Suspend
o Letterbox, Stretch and Center cut-out video display modes
o planned: Video VA-API/Opengl
o planned: Video VDPAU/Opengl
o planned: Video CPU/Xv
o planned: Video CPU/Opengl
o planned: Video decoder VA-API Branch: vaapi-ext/staging
o planned: Video output XvBA / Opengl / Xv
o planned: VA-API grab image
o planned: Improved Software Deinterlacer (yadif or/and ffmpeg filters)
o planned: Video XvBA/XvBA
o planned: software volume, software channel resample
o planned: atmo light support
To compile you must have the 'requires' installed.
@@ -137,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
View File

@@ -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
View File

@@ -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.
**

View File

@@ -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
View File

@@ -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);
}

View File

@@ -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
View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -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);

File diff suppressed because it is too large Load Diff

1981
video.c

File diff suppressed because it is too large Load Diff

36
video.h
View File

@@ -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.
/// @}