16 Commits
0.1.3 ... 0.1.5

Author SHA1 Message Date
Johns
aba14813c0 Add OSS Mixer support. 2012-01-04 16:59:48 +01:00
Johns
0f449c2394 Remove warnings, reduce debug informations. 2012-01-04 16:58:35 +01:00
Johns
9a30d387a1 Crash and thread fixes.
Check if PTS is valid, otherwise debug code does false abort.
Fix bug: audio new stream is not thread safe.
2012-01-03 22:10:04 +01:00
Johns
c8e70ec0fe Audio update.
Alsa: report needed down sampling of 3/5/6 to 2 channels.
Moved alsa code into alsa module.
Initial OSS output support.
2012-01-03 21:42:39 +01:00
Johns
5546354cc7 Version bump. 2012-01-02 19:31:08 +01:00
Johns
442e021d87 Lock av_open/close calls.
New ffmpeg dislikes simultaneous open/close from audio/video.
Prepared audio decoder without av_parser_parse2.
Handle av_audio_resample_init errors.
2012-01-02 17:47:50 +01:00
Johns
4301718329 Documents updates. 2012-01-02 15:27:03 +01:00
Johns
3b7688b78b Print debug only, if compiled with -DDEBUG. 2012-01-02 15:21:58 +01:00
Johns
d2606a5d5f Search audio sync inside PES packets. 2012-01-01 21:14:15 +01:00
Johns
0d63fac2e8 Use only the needed number of surfaces.
Fix problem with video-xvba and too many surfaces used.
Prepare new audio driver "oss".
2011-12-31 18:55:07 +01:00
Johns
c8c760a069 Make pthread_setname_np optional for older glibc. 2011-12-31 17:28:58 +01:00
Johns
0c7170989d Remove warning, when building without vdpau. 2011-12-30 21:50:58 +01:00
Johns
12bfab3f10 Name threads for easier debugging. 2011-12-30 16:47:57 +01:00
Johns
bded2ae5df Prepared vdpau noise reduction support. 2011-12-29 19:45:00 +01:00
Johns
1f2d1d235e Vpdau displays black, when no video available. 2011-12-29 17:47:21 +01:00
Johns
f179264468 Fix bug: CodecVideoDecode destroys avpkt. 2011-12-29 13:43:12 +01:00
11 changed files with 1687 additions and 390 deletions

View File

@@ -1,4 +1,30 @@
User johns
Date:
Release Version 0.1.5
Adds OSS mixer support.
Fix bug: audio new stream is not thread safe.
New audio driver OSS.
Fix bug: needed down sampling of 3/5/6 to 2 channels not reported.
Search audio sync inside PES packets, for insane dvb streams.
Use only the needed number of surfaces.
Date: Thu Dec 29 19:44:43 CET 2011
Release Version 0.1.4
Prepared vdpau noise reduction support.
Vdpau also displays a black surface, when no video is available.
Fix bug: CodecVideoDecode destroys avpkt.
Date: Thu Dec 29 00:55:57 CET 2011
Release Version 0.1.3
Add missing VdpauDecoderDestroy.
Cleanup video packet ringbuffer.
Allow build without VDPAU.
Fix bug: swapped end and start.
Support other than "PCM" alsa mixer channels.
Date: Sat Dec 24 15:26:27 CET 2011
Release Version 0.1.2

View File

@@ -18,8 +18,11 @@ VERSION = $(shell grep 'static const char \*const VERSION *=' $(PLUGIN).cpp | aw
### Configuration (edit this for your needs)
CONFIG := #-DDEBUG
CONFIG += $(shell pkg-config --exists libva && echo "-DUSE_VAAPI")
#CONFIG += -DHAVE_PTHREAD_NAME
CONFIG += $(shell pkg-config --exists vdpau && echo "-DUSE_VDPAU")
CONFIG += $(shell pkg-config --exists libva && echo "-DUSE_VAAPI")
CONFIG += $(shell pkg-config --exists alsa && echo "-DUSE_ALSA")
#CONFIG += -DUSE_OSS
### The C++ compiler and options:
@@ -30,14 +33,27 @@ CFLAGS ?= -g -O2 -W -Wall -Wextra -Winit-self \
-Wdeclaration-after-statement -fPIC
#CFLAGS += -Werror
override CFLAGS += $(DEFINES) $(INCLUDES) \
$(shell pkg-config --cflags alsa libavcodec libavformat)
$(shell pkg-config --cflags libavcodec libavformat) \
`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` \
$(if $(findstring USE_VDPAU,$(CONFIG)), \
`pkg-config --cflags vdpau`) \
$(if $(findstring USE_VAAPI,$(CONFIG)), \
`pkg-config --cflags libva-x11 libva-glx libva`) \
$(if $(findstring USE_ALSA,$(CONFIG)), \
`pkg-config --cflags alsa`)
override LDFLAGS += -lrt \
$(shell pkg-config --libs alsa libavcodec libavformat) \
$(shell pkg-config --libs libavcodec libavformat) \
`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` \
`pkg-config --libs vdpau` \
`pkg-config --libs libva-x11 libva-glx libva`
$(if $(findstring USE_VDPAU,$(CONFIG)), \
`pkg-config --libs vdpau`) \
$(if $(findstring USE_VAAPI,$(CONFIG)), \
`pkg-config --libs libva-x11 libva-glx libva`) \
$(if $(findstring USE_ALSA,$(CONFIG)), \
`pkg-config --libs alsa`)
### The directory environment:

View File

@@ -1,6 +1,6 @@
@file README.txt @brief A software HD output device for VDR
Copyright (c) 2011 by Johns. All Rights Reserved.
Copyright (c) 2011, 2012 by Johns. All Rights Reserved.
Contributor(s):
@@ -20,14 +20,21 @@ $Id$
A software and GPU emulated HD output device plugin for VDR.
o Video VA-API/VA-API
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 planned: Video VA-API/Opengl
o planned: Video VDPAU/Opengl
o planned: Video CPU/Xv
o planned: Video CPU/Opengl
o planned: Software Deinterlacer
o Audio FFMpeg/Analog
o Audio FFMpeg/Digital
o planned: HDMI/SPDIF Passthrough
o planned: Video XvBA/XvBA
o Audio FFMpeg/Alsa/Analog
o Audio FFMpeg/Alsa/Digital
o Audio FFMpeg/OSS/Analog
o planned: Alsa HDMI/SPDIF Passthrough
o planned: OSS HDMI/SPDIF Passthrough
To compile you must have the 'requires' installed.
@@ -57,24 +64,37 @@ Install:
cd vdr-softhddevice
make VDRDIR=<path-to-your-vdr-files> LIBDIR=.
You can edit Makefile to enable/disable VDPAU / VA-API / Alsa / OSS
support.
Setup: environment
------
Following is supported:
DISPLAY=:0.0
x11 display name
only if alsa is configured
ALSA_DEVICE=default
alsa PCM device name
ALSA_MIXER=default
alsa control device name
ALSA_MIXER_CHANNEL=PCM
alsa control channel name
only if oss is configured
OSS_AUDIODEV=/dev/dsp
oss dsp device name
OSS_MIXERDEV=/dev/mixer
oss mixer device name
OSS_MIXER_CHANNEL=pcm
oss mixer channel name
Setup: /etc/vdr/setup.conf
------
Following is supported:
softhddevice.Deinterlace = 0
0 = bob, 1 = weave, 2 = temporal, 3 = temporal_spatial, 4 = software
(only 0, 1 supported)
(only 0, 1 supported with vaapi)
softhddevice.MakePrimary = 1
0 = no change, 1 make softhddevice primary at start
@@ -85,15 +105,27 @@ Setup: /etc/vdr/setup.conf
softhddevice.AudioDelay = 0
+n or -n ms
Commandline:
------------
Use vdr -h to see the command line arguments support by the plugin.
Warning:
--------
libav is not supported, expect many bugs with it.
Requires:
---------
media-video/ffmpeg
media-video/ffmpeg (version >=0.7)
Complete solution to record, convert and stream audio and
video. Includes libavcodec.
http://ffmpeg.org
media-libs/alsa-lib
Advanced Linux Sound Architecture Library
http://www.alsa-project.org
or
kernel support for oss/oss4 or alsa oss emulation
x11-libs/libva
Video Acceleration (VA) API for Linux
http://www.freedesktop.org/wiki/Software/vaapi
@@ -104,7 +136,7 @@ Requires:
x11-libs/vdpau-video
VDPAU Backend for Video Acceleration (VA) API
http://www.freedesktop.org/wiki/Software/vaapi
or untested
or
x11-libs/xvba-video
XVBA Backend for Video Acceleration (VA) API
http://www.freedesktop.org/wiki/Software/vaapi
@@ -116,7 +148,7 @@ Requires:
x11-libs/xcb-util-keysyms
X C-language Bindings library
http://xcb.freedesktop.org
Only versions >= 0.3.8 are supported
Only versions >= 0.3.8 are good supported
x11-libs/libX11
X.Org X11 library

50
Todo
View File

@@ -1,3 +1,23 @@
@file Todo @brief A software HD output device for VDR
Copyright (c) 2011, 2012 by Johns. All Rights Reserved.
Contributor(s):
License: AGPLv3
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
$Id: $
missing:
video out with xv
video out with opengl
@@ -7,11 +27,14 @@ missing:
atmolight
zoom/fit-zoom 4:3
multistream handling
HDMI/SPDIF Passthrough
disable screensaver
disable window cursor
vdpau:
1080i with temporal spatial too slow GT 520
1080i with temporal spatial and level 1 scaling too slow with GT 520
1080i with temporal spatial too slow with GT 520 on some channels
VdpPreemptionCallback handling
Loose a surface
libva-intel-driver:
intel still has hangups most with 1080i
@@ -25,7 +48,6 @@ libva-vdpau-driver:
-> vaDestroyContext -> pthread_rwlock_wrlock)
libva-xvba-driver:
mpeg1/2 needs software decoder fixed
x11:
support resize of x11 window
@@ -35,19 +57,27 @@ x11:
disable cursor
audio/alsa:
video/audio asyncron
random crash in av_parser_parse2, when switching channels
done? video/audio asyncron
random crashes in av_parser_parse2, when switching channels
sometimes alsa hangs
playback of >2 channels on 2 channel hardware
done?
better downmix of >2 channels on 2 channel hardware
remix support of unsupported sample rates
libav supports only resample of mono to 2 channels
ffmpeg didn't support resample of 5 to 2 channels
CodecAudioOpen can fail "can't open audio codec" and does Fatal exit.
on some channels it takes long time until sound can be heared.
this channels has packet start not at the beginning of the start packet
audio/oss:
alsa oss emulation mixer "pcm" not working
ring buffer overflow with alsa oss emulation
playback of recording
play back is too fast
setup:
Setup menu parameters aren't automatic loaded?
Setup of decoder type.
Setup of output type.
Setup of display type.
Setup 4:3 zoom type
Setup parameters are not used until restart.
Can a notice be added to the setup menu?

1166
audio.c

File diff suppressed because it is too large Load Diff

276
codec.c
View File

@@ -1,7 +1,7 @@
///
/// @file codec.c @brief Codec functions
///
/// Copyright (c) 2009 - 2011 by Johns. All Rights Reserved.
/// Copyright (c) 2009 - 2012 by Johns. All Rights Reserved.
///
/// Contributor(s):
///
@@ -26,6 +26,14 @@
/// This module contains all decoder and codec functions.
/// It is uses ffmpeg (http://ffmpeg.org) as backend.
///
/// It may work with libav (http://libav.org), but the tests show
/// many bugs and incompatiblity in it. Don't use this shit.
///
/**
** use av_parser to support insane dvb audio streams.
*/
#define USE_AVPARSER
#include <stdio.h>
#include <unistd.h>
@@ -44,6 +52,11 @@
#include <libavcodec/vdpau.h>
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <pthread.h>
#ifdef MAIN_H
#include MAIN_H
#endif
@@ -52,6 +65,18 @@
#include "audio.h"
#include "codec.h"
//----------------------------------------------------------------------------
// Global
//----------------------------------------------------------------------------
///
/// ffmpeg lock mutex
///
/// new ffmpeg dislikes simultanous open/close
/// this breaks our code, until this is fixed use lock.
///
static pthread_mutex_t CodecLockMutex;
//----------------------------------------------------------------------------
// Video
//----------------------------------------------------------------------------
@@ -348,16 +373,20 @@ void CodecVideoOpen(VideoDecoder * decoder, const char *name, int codec_id)
}
// FIXME: for software decoder use all cpus, otherwise 1
decoder->VideoCtx->thread_count = 1;
pthread_mutex_lock(&CodecLockMutex);
// open codec
#if LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(53,5,0)
if (avcodec_open(decoder->VideoCtx, video_codec) < 0) {
pthread_mutex_unlock(&CodecLockMutex);
Fatal(_("codec: can't open video codec!\n"));
}
#else
if (avcodec_open2(decoder->VideoCtx, video_codec, NULL) < 0) {
pthread_mutex_unlock(&CodecLockMutex);
Fatal(_("codec: can't open video codec!\n"));
}
#endif
pthread_mutex_unlock(&CodecLockMutex);
decoder->VideoCtx->opaque = decoder; // our structure
@@ -436,7 +465,9 @@ void CodecVideoClose(VideoDecoder * video_decoder)
// FIXME: play buffered data
av_freep(&video_decoder->Frame);
if (video_decoder->VideoCtx) {
pthread_mutex_lock(&CodecLockMutex);
avcodec_close(video_decoder->VideoCtx);
pthread_mutex_unlock(&CodecLockMutex);
av_freep(&video_decoder->VideoCtx);
}
}
@@ -446,9 +477,9 @@ void CodecVideoClose(VideoDecoder * video_decoder)
/**
** Display pts...
**
** ffmpeg 0.9 pts always AV_NOPTS_VALUE
** ffmpeg 0.9 pkt_pts nice monotonic (only with HD)
** ffmpeg 0.9 pkt_dts wild jumping -160 - 340 ms
** ffmpeg-0.9 pts always AV_NOPTS_VALUE
** ffmpeg-0.9 pkt_pts nice monotonic (only with HD)
** ffmpeg-0.9 pkt_dts wild jumping -160 - 340 ms
**
** libav 0.8_pre20111116 pts always AV_NOPTS_VALUE
** libav 0.8_pre20111116 pkt_pts always 0 (could be fixed?)
@@ -483,32 +514,28 @@ void DisplayPts(AVCodecContext * video_ctx, AVFrame * frame)
**
** @param decoder video decoder data
** @param avpkt video packet
**
** @note this version destroys avpkt!!
*/
void CodecVideoDecode(VideoDecoder * decoder, AVPacket * avpkt)
void CodecVideoDecode(VideoDecoder * decoder, const AVPacket * avpkt)
{
AVCodecContext *video_ctx;
AVFrame *frame;
int used;
int got_frame;
AVPacket pkt[1];
video_ctx = decoder->VideoCtx;
frame = decoder->Frame;
*pkt = *avpkt; // use copy
next_part:
// FIXME: this function can crash with bad packets
used = avcodec_decode_video2(video_ctx, frame, &got_frame, avpkt);
Debug(4, "%s: %p %d -> %d %d\n", __FUNCTION__, avpkt->data, avpkt->size,
used, got_frame);
used = avcodec_decode_video2(video_ctx, frame, &got_frame, pkt);
Debug(4, "%s: %p %d -> %d %d\n", __FUNCTION__, pkt->data, pkt->size, used,
got_frame);
if (got_frame) { // frame completed
//DisplayPts(video_ctx, frame);
if (video_ctx->hwaccel_context) {
VideoRenderFrame(decoder->HwDecoder, video_ctx, frame);
} else {
VideoRenderFrame(decoder->HwDecoder, video_ctx, frame);
}
VideoRenderFrame(decoder->HwDecoder, video_ctx, frame);
} else {
// some frames are needed for references, interlaced frames ...
// could happen with h264 dvb streams, just drop data.
@@ -516,23 +543,18 @@ void CodecVideoDecode(VideoDecoder * decoder, AVPacket * avpkt)
Debug(4, "codec: %8d incomplete interlaced frame %d bytes used\n",
video_ctx->frame_number, used);
}
if (used != avpkt->size) {
if (used == 0) {
goto next_part;
}
if (used != pkt->size) {
if (used >= 0) {
// some tv channels, produce this
Debug(4,
"codec: ooops didn't use complete video packet used %d of %d\n",
used, avpkt->size);
avpkt->data += used;
avpkt->size -= used;
used, pkt->size);
pkt->data += used;
pkt->size -= used;
goto next_part;
}
Debug(3, "codec: bad frame %d\n", used);
}
return;
}
//----------------------------------------------------------------------------
@@ -554,7 +576,7 @@ struct _audio_decoder_
AVCodec *AudioCodec; ///< audio codec
AVCodecContext *AudioCtx; ///< audio codec context
/// audio parser to support wired dvb streaks
/// audio parser to support insane dvb streaks
AVCodecParserContext *AudioParser;
int SampleRate; ///< current stream sample rate
int Channels; ///< current stream channels
@@ -606,16 +628,20 @@ 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"));
}
pthread_mutex_lock(&CodecLockMutex);
// open codec
#if LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(53,5,0)
if (avcodec_open(audio_decoder->AudioCtx, audio_codec) < 0) {
pthread_mutex_unlock(&CodecLockMutex);
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"));
}
#endif
pthread_mutex_unlock(&CodecLockMutex);
Debug(3, "codec: audio '%s'\n", audio_decoder->AudioCtx->codec_name);
if (audio_codec->capabilities & CODEC_CAP_TRUNCATED) {
@@ -650,20 +676,24 @@ void CodecAudioClose(AudioDecoder * audio_decoder)
audio_decoder->AudioParser = NULL;
}
if (audio_decoder->AudioCtx) {
pthread_mutex_lock(&CodecLockMutex);
avcodec_close(audio_decoder->AudioCtx);
pthread_mutex_unlock(&CodecLockMutex);
av_freep(&audio_decoder->AudioCtx);
}
}
#ifdef USE_AVPARSER
/**
** Decode an audio packet.
**
** PTS must be handled self.
**
** @param audio_decoder audio_Decoder data
** @param audio_decoder audio decoder data
** @param avpkt audio packet
*/
void CodecAudioDecode(AudioDecoder * audio_decoder, AVPacket * avpkt)
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)));
@@ -708,7 +738,7 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, AVPacket * avpkt)
buf_sz = sizeof(buf);
l = avcodec_decode_audio3(audio_ctx, buf, &buf_sz, dpkt);
if (l < 0) { // no audio frame could be decompressed
Error(_("codec: error audio data\n"));
Error(_("codec: error audio data at %d\n"), index);
break;
}
#ifdef notyetFF_API_OLD_DECODE_AUDIO
@@ -741,8 +771,10 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, AVPacket * avpkt)
if ((err =
AudioSetup(&audio_decoder->HwSampleRate,
&audio_decoder->HwChannels))) {
Debug(3, "codec/audio: resample %d -> %d\n",
audio_ctx->channels, audio_decoder->HwChannels);
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 =
@@ -750,10 +782,18 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, AVPacket * avpkt)
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;
}
}
}
@@ -766,10 +806,25 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, AVPacket * avpkt)
__attribute__ ((aligned(16)));
int outlen;
// FIXME: libav-0.7.2 crash here
outlen =
audio_resample(audio_decoder->ReSample, outbuf, buf,
buf_sz);
AudioEnqueue(outbuf, outlen);
#ifdef DEBUG
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);
AudioEnqueue(outbuf, outlen);
}
} else {
AudioEnqueue(buf, buf_sz);
}
@@ -784,10 +839,167 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, AVPacket * avpkt)
}
#if 1
// or av_free_packet, make no difference here
av_destruct_packet(spkt);
#endif
}
#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
if (buf_sz > 0) { // something decoded
// Update audio clock
if ((uint64_t) spkt->pts != AV_NOPTS_VALUE) {
AudioSetClock(spkt->pts);
spkt->pts = AV_NOPTS_VALUE;
}
// FIXME: must first play remainings bytes, than change and play new.
if (audio_decoder->SampleRate != audio_ctx->sample_rate
|| audio_decoder->Channels != audio_ctx->channels) {
int err;
if (audio_decoder->ReSample) {
audio_resample_close(audio_decoder->ReSample);
audio_decoder->ReSample = NULL;
}
audio_decoder->SampleRate = audio_ctx->sample_rate;
audio_decoder->HwSampleRate = audio_ctx->sample_rate;
audio_decoder->Channels = audio_ctx->channels;
audio_decoder->HwChannels = audio_ctx->channels;
// channels not support?
if ((err =
AudioSetup(&audio_decoder->HwSampleRate,
&audio_decoder->HwChannels))) {
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 {
// FIXME: handle errors
Debug(3, "codec/audio: audio setup error\n");
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);
#ifdef DEBUG
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_ctx->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);
AudioEnqueue(outbuf, outlen);
}
} else {
AudioEnqueue(buf, buf_sz);
}
}
}
index += n;
}
#if 1
// or av_free_packet, make no difference here
av_destruct_packet(spkt);
#endif
}
#endif
//----------------------------------------------------------------------------
// Codec
//----------------------------------------------------------------------------
@@ -807,6 +1019,7 @@ static void CodecNoopCallback( __attribute__ ((unused))
*/
void CodecInit(void)
{
pthread_mutex_init(&CodecLockMutex, NULL);
#ifndef DEBUG
// disable display ffmpeg error messages
av_log_set_callback(CodecNoopCallback);
@@ -821,4 +1034,5 @@ void CodecInit(void)
*/
void CodecExit(void)
{
pthread_mutex_destroy(&CodecLockMutex);
}

View File

@@ -47,7 +47,7 @@ extern void CodecVideoOpen(VideoDecoder *, const char *, int);
extern void CodecVideoClose(VideoDecoder *);
/// Decode a video packet
extern void CodecVideoDecode(VideoDecoder *, AVPacket * pkt);
extern void CodecVideoDecode(VideoDecoder *, const AVPacket * pkt);
/// Allocate a new audio decoder context.
extern AudioDecoder *CodecAudioNewDecoder(void);
@@ -59,7 +59,7 @@ extern void CodecAudioOpen(AudioDecoder *, const char *, int);
extern void CodecAudioClose(AudioDecoder *);
/// Decode an audio packet
extern void CodecAudioDecode(AudioDecoder *, AVPacket * pkt);
extern void CodecAudioDecode(AudioDecoder *, const AVPacket * pkt);
/// Setup and initialize codec module.
extern void CodecInit(void);

27
misc.h
View File

@@ -1,7 +1,7 @@
///
/// @file misc.h @brief Misc function header file
///
/// Copyright (c) 2009 - 2011 by Lutz Sammer. All Rights Reserved.
/// Copyright (c) 2009 - 2012 by Lutz Sammer. All Rights Reserved.
///
/// Contributor(s):
/// Copied from uwm.
@@ -46,24 +46,28 @@ extern int SysLogLevel; ///< how much information wanted
// Prototypes
//////////////////////////////////////////////////////////////////////////////
static inline void Debug(const int, const char *format, ...)
static inline void Syslog(const int, const char *format, ...)
__attribute__ ((format(printf, 2, 3)));
//////////////////////////////////////////////////////////////////////////////
// Inlines
//////////////////////////////////////////////////////////////////////////////
#ifdef DEBUG
#define DebugLevel 4 /// private debug level
#else
#define DebugLevel 0 /// private debug level
#endif
/**
** Debug output function.
** Syslog output function.
**
** - 0 fatal errors and errors
** - 1 warnings
** - 2 info
** - 3 important debug and fixme's
*/
static inline void Debug(const int level, const char *format, ...)
static inline void Syslog(const int level, const char *format, ...)
{
if (SysLogLevel > level || DebugLevel > level) {
va_list ap;
@@ -77,7 +81,7 @@ static inline void Debug(const int level, const char *format, ...)
/**
** Show error.
*/
#define Error(fmt...) Debug(0, fmt)
#define Error(fmt...) Syslog(0, fmt)
/**
** Show fatal error.
@@ -87,12 +91,21 @@ static inline void Debug(const int level, const char *format, ...)
/**
** Show warning.
*/
#define Warning(fmt...) Debug(1, fmt)
#define Warning(fmt...) Syslog(1, fmt)
/**
** Show info.
*/
#define Info(fmt...) Debug(2, fmt)
#define Info(fmt...) Syslog(2, fmt)
/**
** Show debug.
*/
#ifdef DEBUG
#define Debug(level, fmt...) Syslog(level, fmt)
#else
#define Debug(level, fmt...) /* disabled */
#endif
/**
** Get ticks in ms.

View File

@@ -1,7 +1,7 @@
///
/// @file softhddev.c @brief A software HD device plugin for VDR.
///
/// Copyright (c) 2011 by Johns. All Rights Reserved.
/// Copyright (c) 2011, 2012 by Johns. All Rights Reserved.
///
/// Contributor(s):
///
@@ -44,17 +44,138 @@
static char BrokenThreadsAndPlugins; ///< broken vdr threads and plugins
#ifdef USE_VDPAU
static char ConfigVdpauDecoder = 1; ///< use vdpau decoder, if possible
#else
#define ConfigVdpauDecoder 0 ///< no vdpau decoder configured
#endif
//////////////////////////////////////////////////////////////////////////////
// Audio
//////////////////////////////////////////////////////////////////////////////
static volatile char NewAudioStream; ///< new audio stream
static AudioDecoder *MyAudioDecoder; ///< audio decoder
static enum CodecID AudioCodecID; ///< current codec id
extern void AudioTest(void); // FIXME:
/**
** mpeg bitrate table.
**
** BitRateTable[Version][Layer][Index]
*/
static const uint16_t BitRateTable[2][4][16] = {
// MPEG Version 1
{{},
{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,
0},
{0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}},
// MPEG Version 2 & 2.5
{{},
{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}
}
};
/**
** mpeg samperate table.
*/
static const uint16_t SampleRateTable[4] = {
44100, 48000, 32000, 0
};
/**
** Find sync in audio packet.
**
** @param avpkt audio packet
**
** From: http://www.mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm
**
** AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
**
** o a 11x frame sync
** o b 2x mpeg audio version (2.5, reserved, 2, 1)
** o c 2x layer (reserved, III, II, I)
** o e 2x BitRate index
** o f 2x SampleRate index
** o g 1x Paddding bit
** o .. doesn't care
**
** frame length:
** Layer I:
** FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4
** Layer II & III:
** FrameLengthInBytes = 144 * BitRate / SampleRate + Padding
*/
static int FindAudioSync(const AVPacket * avpkt)
{
int i;
const uint8_t *data;
i = 0;
data = avpkt->data;
while (i < avpkt->size - 4) {
if (data[i] == 0xFF && (data[i + 1] & 0xFC) == 0xFC) {
int mpeg2;
int mpeg25;
int layer;
int bit_rate_index;
int sample_rate_index;
int padding;
int bit_rate;
int sample_rate;
int frame_size;
mpeg2 = !(data[i + 1] & 0x08) && (data[i + 1] & 0x10);
mpeg25 = !(data[i + 1] & 0x08) && !(data[i + 1] & 0x10);
layer = 4 - ((data[i + 1] >> 1) & 0x03);
bit_rate_index = (data[i + 2] >> 4) & 0x0F;
sample_rate_index = (data[i + 2] >> 2) & 0x03;
padding = (data[i + 2] >> 1) & 0x01;
sample_rate = SampleRateTable[sample_rate_index];
if (!sample_rate) { // no valid sample rate try next
i++;
continue;
}
sample_rate >>= mpeg2; // mpeg 2 half rate
sample_rate >>= mpeg25; // mpeg 2.5 quarter rate
bit_rate = BitRateTable[mpeg2 | mpeg25][layer][bit_rate_index];
bit_rate *= 1000;
switch (layer) {
case 1:
frame_size = (12 * bit_rate) / sample_rate;
frame_size = (frame_size + padding) * 4;
break;
case 2:
case 3:
default:
frame_size = (144 * bit_rate) / sample_rate;
frame_size = frame_size + padding;
break;
}
Debug(3,
"audio: mpeg%s layer%d bitrate=%d samplerate=%d %d bytes\n",
mpeg25 ? "2.5" : mpeg2 ? "2" : "1", layer, bit_rate,
sample_rate, frame_size);
if (i + frame_size < avpkt->size - 4) {
if (data[i + frame_size] == 0xFF
&& (data[i + frame_size + 1] & 0xFC) == 0xFC) {
Debug(3, "audio: mpeg1/2 found at %d\n", i);
return i;
}
}
// no valid frame size or no continuation, try next
}
i++;
}
return -1;
}
/**
** Play audio packet.
**
@@ -62,7 +183,8 @@ extern void AudioTest(void); // FIXME:
** @param size size of PES packet
** @param id PES packet type
*/
void PlayAudio(const uint8_t * data, int size, uint8_t id)
void PlayAudio(const uint8_t * data, int size,
__attribute__ ((unused)) uint8_t id)
{
int n;
AVPacket avpkt[1];
@@ -70,13 +192,16 @@ void PlayAudio(const uint8_t * data, int size, uint8_t id)
if (BrokenThreadsAndPlugins) {
return;
}
// PES header 0x00 0x00 0x01 ID
// ID 0xBD 0xC0-0xCF
// channel switch: SetAudioChannelDevice: SetDigitalAudioDevice:
// Detect audio code
// MPEG-PS mp2 MPEG1, MPEG2, AC3
if (NewAudioStream) {
// FIXME: does this clear the audio ringbuffer?
CodecAudioClose(MyAudioDecoder);
AudioCodecID = CODEC_ID_NONE;
NewAudioStream = 0;
}
// PES header 0x00 0x00 0x01 ID
// ID 0xBD 0xC0-0xCF
if (size < 9) {
Error(_("[softhddev] invalid audio packet\n"));
@@ -107,6 +232,9 @@ void PlayAudio(const uint8_t * data, int size, uint8_t id)
Error(_("[softhddev] invalid audio packet\n"));
return;
}
// Detect audio code
// MPEG-PS mp2 MPEG1, MPEG2, AC3
// Syncword - 0x0B77
if (data[0] == 0x0B && data[1] == 0x77) {
if (!MyAudioDecoder) {
@@ -137,9 +265,23 @@ void PlayAudio(const uint8_t * data, int size, uint8_t id)
// no start package
// FIXME: Nick/Viva sends this shit, need to find sync in packet
// FIXME: otherwise it takes too long until sound appears
if (AudioCodecID == CODEC_ID_NONE) {
Debug(3, "[softhddev]%s: ??? %d\n", __FUNCTION__, id);
return;
avpkt->data = (void *)data;
avpkt->size = size;
n = FindAudioSync(avpkt);
if (n < 0) {
return;
}
if (!MyAudioDecoder) {
MyAudioDecoder = CodecAudioNewDecoder();
}
CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_MP2);
AudioCodecID = CODEC_ID_MP2;
data += n;
size -= n;
}
}
@@ -184,13 +326,13 @@ void SetVolumeDevice(int volume)
#include <alsa/iatomic.h> // portable atomic_t
uint32_t VideoSwitch;
static int NewVideoStream; ///< new video stream
uint32_t VideoSwitch; ///< debug video switch ticks
static volatile char NewVideoStream; ///< new video stream
static VideoDecoder *MyVideoDecoder; ///< video decoder
static enum CodecID VideoCodecID; ///< current codec id
static const char *X11DisplayName; ///< x11 display name
static volatile int Usr1Signal; ///< true got usr1 signal
static volatile char Usr1Signal; ///< true got usr1 signal
/// video PES buffer default size
#define VIDEO_BUFFER_SIZE (512 * 1024)
@@ -200,8 +342,8 @@ static AVPacket VideoPacketRb[VIDEO_PACKET_MAX];
static int VideoPacketWrite; ///< write pointer
static int VideoPacketRead; ///< read pointer
static atomic_t VideoPacketsFilled; ///< how many of the buffer is used
static char VideoFreezed; ///< video freezed
static char VideoClearBuffers; ///< clear video buffers
static volatile char VideoFreezed; ///< video freezed
static volatile char VideoClearBuffers; ///< clear video buffers
#ifdef DEBUG
static int VideoMaxPacketSize; ///< biggest used packet buffer
@@ -277,6 +419,9 @@ static void VideoEnqueue(int64_t pts, const void *data, int size)
/ (VIDEO_BUFFER_SIZE / 2)) * (VIDEO_BUFFER_SIZE / 2));
#ifdef DEBUG
if (avpkt->size <= avpkt->stream_index + size) {
fprintf(stderr, "%d %d %d\n", avpkt->size, avpkt->stream_index,
size);
fflush(stderr);
abort();
}
#endif
@@ -376,6 +521,7 @@ int VideoDecode(void)
CodecVideoClose(MyVideoDecoder);
goto skip;
}
goto skip;
break;
case CODEC_ID_MPEG2VIDEO:
if (last_codec_id != CODEC_ID_MPEG2VIDEO) {
@@ -434,6 +580,44 @@ static void StartVideo(void)
VideoPacketInit();
}
#ifdef DEBUG
/**
** Validate mpeg video packet.
**
** Function to validate a mpeg packet, not needed.
*/
static int ValidateMpeg(const uint8_t * data, int size)
{
int pes_l;
do {
if (size < 9) {
return -1;
}
if (data[0] || data[1] || data[2] != 0x01) {
printf("%02x: %02x %02x %02x %02x %02x\n", data[-1], data[0],
data[1], data[2], data[3], data[4]);
return -1;
}
pes_l = (data[4] << 8) | data[5];
if (!pes_l) { // contains unknown length
return 1;
}
if (6 + pes_l > size) {
return -1;
}
data += 6 + pes_l;
size -= 6 + pes_l;
} while (size);
return 0;
}
#endif
/**
** Play video packet.
**
@@ -463,7 +647,7 @@ int PlayVideo(const uint8_t * data, int size)
if (!MyVideoDecoder) { // no x11 video started
return size;
}
if (NewVideoStream) {
if (NewVideoStream) { // channel switched
Debug(3, "video: new stream %d\n", GetMsTicks() - VideoSwitch);
// FIXME: hack to test results
if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 1) {
@@ -482,13 +666,15 @@ int PlayVideo(const uint8_t * data, int size)
}
n = data[8]; // header size
// wrong size
if (size < 9 + n) {
if (size < 9 + n + 4) {
Error(_("[softhddev] invalid video packet\n"));
return size;
}
check = data + 9 + n;
// FIXME: get pts/dts, when we need it
// FIXME: hack to test results
if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 1) {
return 0;
}
// get pts/dts
pts = AV_NOPTS_VALUE;
if (data[7] & 0x80) {
@@ -496,6 +682,10 @@ int PlayVideo(const uint8_t * data, int size)
(int64_t) (data[9] & 0x0E) << 29 | data[10] << 22 | (data[11] &
0xFE) << 14 | data[12] << 7 | (data[13] & 0xFE) >> 1;
#ifdef DEBUG
if (!(data[13] & 1) || !(data[11] & 1) || !(data[9] & 1)) {
Error(_("[softhddev] invalid pts in video packet\n"));
return size;
}
//Debug(3, "video: pts %#012" PRIx64 "\n", pts);
if (data[13] != (((pts & 0x7F) << 1) | 1)) {
abort();
@@ -509,35 +699,36 @@ int PlayVideo(const uint8_t * data, int size)
if (data[10] != ((pts >> 22) & 0xFF)) {
abort();
}
if ((data[9] & 0x0F) != (((pts >> 30) << 1) | 1)) {
abort();
}
#endif
}
// FIXME: no valid mpeg2/h264 detection yet
check = data + 9 + n;
if (0) {
printf("%02x: %02x %02x %02x %02x %02x\n", data[6], check[0], check[1],
check[2], check[3], check[4]);
}
// PES_VIDEO_STREAM 0xE0 or PES start code
if ((data[6] & 0xC0) != 0x80 || (!check[0] && !check[1]
&& check[2] == 0x1)) {
//(data[6] & 0xC0) != 0x80 ||
if ((!check[0] && !check[1] && check[2] == 0x1)) {
if (VideoCodecID == CODEC_ID_MPEG2VIDEO) {
// FIXME: hack to test results
if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 1) {
return 0;
}
VideoNextPacket(CODEC_ID_MPEG2VIDEO);
} else {
Debug(3, "video: mpeg2 detected\n");
Debug(3, "video: mpeg2 detected ID %02x\n", check[3]);
VideoCodecID = CODEC_ID_MPEG2VIDEO;
}
#ifdef DEBUG
if (ValidateMpeg(data, size)) {
Debug(3, "softhddev/video: invalid mpeg2 video packet\n");
}
#endif
// Access Unit Delimiter
} else if (!check[0] && !check[1] && !check[2] && check[3] == 0x1
&& check[4] == 0x09) {
} else if ((data[6] & 0xC0) == 0x80 && !check[0] && !check[1]
&& !check[2] && check[3] == 0x1 && check[4] == 0x09) {
if (VideoCodecID == CODEC_ID_H264) {
// FIXME: hack to test results
if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 1) {
return 0;
}
VideoNextPacket(CODEC_ID_H264);
} else {
Debug(3, "video: h264 detected\n");
@@ -550,11 +741,8 @@ int PlayVideo(const uint8_t * data, int size)
return size;
}
if (VideoCodecID == CODEC_ID_MPEG2VIDEO) {
// mpeg codec supports incomplete packages
// FIXME: hack to test results
if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 1) {
return 0;
}
// mpeg codec supports incomplete packets
// waiting for a full complete packages, increases needed delays
VideoNextPacket(CODEC_ID_MPEG2VIDEO);
}
}
@@ -582,9 +770,7 @@ void SetPlayMode(void)
}
}
if (MyAudioDecoder) {
// FIXME: does this clear the audio ringbuffer?
CodecAudioClose(MyAudioDecoder);
AudioCodecID = CODEC_ID_NONE;
NewAudioStream = 1;
}
}
@@ -881,10 +1067,12 @@ void Stop(void)
// no it doesn't do a good thread cleanup
if (MyVideoDecoder) {
CodecVideoClose(MyVideoDecoder);
// FIXME: CodecDelVideoDecoder(MyVideoDecoder);
MyVideoDecoder = NULL;
}
if (MyAudioDecoder) {
CodecAudioClose(MyAudioDecoder);
// FIXME: CodecDelAudioDecoder(MyAudioDecoder);
MyAudioDecoder = NULL;
}

View File

@@ -1,7 +1,7 @@
///
/// @file softhddevice.cpp @brief A software HD device plugin for VDR.
///
/// Copyright (c) 2011 by Johns. All Rights Reserved.
/// Copyright (c) 2011, 2012 by Johns. All Rights Reserved.
///
/// Contributor(s):
///
@@ -35,11 +35,12 @@
#include "softhddevice.h"
extern "C" {
#include "video.h"
extern void AudioPoller(void);
}
//////////////////////////////////////////////////////////////////////////////
static const char *const VERSION = "0.1.3";
static const char *const VERSION = "0.1.5";
static const char *const DESCRIPTION =
trNOOP("A software and GPU emulated HD device");
@@ -332,6 +333,9 @@ class cSoftHdDevice:public cDevice
virtual void GetOsdSize(int &, int &, double &);
virtual int PlayVideo(const uchar *, int);
//virtual int PlayTsVideo(const uchar *, int);
#ifdef USE_OSS // FIXME: testing only oss
virtual int PlayTsAudio(const uchar *, int);
#endif
virtual void SetAudioChannelDevice(int);
virtual int GetAudioChannelDevice(void);
virtual void SetDigitalAudioDevice(bool);
@@ -569,6 +573,20 @@ int cSoftHdDevice::PlayTsVideo(const uchar * Data, int Length)
}
#endif
#ifdef USE_OSS // FIXME: testing only oss
///
/// Play a TS audio packet.
///
/// misuse this function as audio poller
///
int cSoftHdDevice::PlayTsAudio(const uchar * data, int length)
{
AudioPoller();
return cDevice::PlayTsAudio(data,length);
}
#endif
uchar *cSoftHdDevice::GrabImage(int &size, bool jpeg, int quality, int sizex,
int sizey)
{

164
video.c
View File

@@ -68,6 +68,10 @@
#endif
#include <pthread.h>
#include <time.h>
#ifndef HAVE_PTHREAD_NAME
/// only available with newer glibc
#define pthread_setname_np(thread, name)
#endif
#endif
#ifdef USE_XLIB_XCB
@@ -163,7 +167,11 @@ typedef enum _video_zoom_modes_
//----------------------------------------------------------------------------
#define CODEC_SURFACES_MAX 31 ///< maximal of surfaces
#define CODEC_SURFACES_DEFAULT (21+4) ///< default of surfaces
// FIXME: video-xvba only supports 14
#define xCODEC_SURFACES_DEFAULT 14 ///< default of surfaces
#define CODEC_SURFACES_MPEG2 3 ///< 1 decode, up to 2 references
#define CODEC_SURFACES_MPEG4 3 ///< 1 decode, up to 2 references
#define CODEC_SURFACES_H264 21 ///< 1 decode, up to 20 references
@@ -756,6 +764,7 @@ struct _vaapi_decoder_
struct vaapi_context VaapiContext[1]; ///< ffmpeg VA-API context
int SurfacesNeeded; ///< number of surface to request
int SurfaceUsedN; ///< number of used surfaces
/// used surface ids
VASurfaceID SurfacesUsed[CODEC_SURFACES_MAX];
@@ -815,11 +824,16 @@ static void VaapiBlackSurface(VaapiDecoder * decoder);
///
static void VaapiCreateSurfaces(VaapiDecoder * decoder, int width, int height)
{
#ifdef DEBUG
if (!decoder->SurfacesNeeded) {
Error(_("video/vaapi: surface needed not set\n"));
decoder->SurfacesNeeded = 3 + VIDEO_SURFACES_MAX;
}
#endif
Debug(3, "video/vaapi: %s: %dx%d * %d\n", __FUNCTION__, width, height,
CODEC_SURFACES_DEFAULT);
decoder->SurfacesNeeded);
// FIXME: allocate only the number of needed surfaces
decoder->SurfaceFreeN = CODEC_SURFACES_DEFAULT;
decoder->SurfaceFreeN = decoder->SurfacesNeeded;
// VA_RT_FORMAT_YUV420 VA_RT_FORMAT_YUV422 VA_RT_FORMAT_YUV444
if (vaCreateSurfaces(decoder->VaDisplay, width, height,
VA_RT_FORMAT_YUV420, decoder->SurfaceFreeN,
@@ -868,7 +882,7 @@ static void VaapiCreateSurfaces(VaapiDecoder * decoder, int width, int height)
Error(_("video/vaapi: can't associate subpicture\n"));
}
for (i = 0; i < decoder->SurfaceFreeN; ++i) {
Debug(3, "video/vaapi: associate %08x\n",
Debug(4, "video/vaapi: associate %#010x surface\n",
decoder->SurfacesFree[i]);
}
}
@@ -988,6 +1002,9 @@ static void VaapiPrintFrames(const VaapiDecoder * decoder)
Debug(3, "video/vaapi: %d missed, %d duped, %d dropped frames of %d\n",
decoder->FramesMissed, decoder->FramesDuped, decoder->FramesDropped,
decoder->FrameCounter);
#ifndef DEBUG
(void)decoder;
#endif
}
///
@@ -1479,14 +1496,19 @@ static enum PixelFormat Vaapi_get_format(VaapiDecoder * decoder,
// check profile
switch (video_ctx->codec_id) {
case CODEC_ID_MPEG2VIDEO:
decoder->SurfacesNeeded =
CODEC_SURFACES_MPEG2 + VIDEO_SURFACES_MAX;
p = VaapiFindProfile(profiles, profile_n, VAProfileMPEG2Main);
break;
case CODEC_ID_MPEG4:
case CODEC_ID_H263:
decoder->SurfacesNeeded =
CODEC_SURFACES_MPEG4 + VIDEO_SURFACES_MAX;
p = VaapiFindProfile(profiles, profile_n,
VAProfileMPEG4AdvancedSimple);
break;
case CODEC_ID_H264:
decoder->SurfacesNeeded = CODEC_SURFACES_H264 + VIDEO_SURFACES_MAX;
// try more simple formats, fallback to better
if (video_ctx->profile == FF_PROFILE_H264_BASELINE) {
p = VaapiFindProfile(profiles, profile_n,
@@ -1503,9 +1525,11 @@ static enum PixelFormat Vaapi_get_format(VaapiDecoder * decoder,
}
break;
case CODEC_ID_WMV3:
decoder->SurfacesNeeded = CODEC_SURFACES_VC1 + VIDEO_SURFACES_MAX;
p = VaapiFindProfile(profiles, profile_n, VAProfileVC1Main);
break;
case CODEC_ID_VC1:
decoder->SurfacesNeeded = CODEC_SURFACES_VC1 + VIDEO_SURFACES_MAX;
p = VaapiFindProfile(profiles, profile_n, VAProfileVC1Advanced);
break;
default:
@@ -1620,6 +1644,7 @@ static enum PixelFormat Vaapi_get_format(VaapiDecoder * decoder,
slow_path:
// no accelerated format found
decoder->SurfacesNeeded = 1 + VIDEO_SURFACES_MAX;
video_ctx->hwaccel_context = NULL;
return avcodec_default_get_format(video_ctx, fmt);
}
@@ -1950,7 +1975,7 @@ static void VaapiQueueSurface(VaapiDecoder * decoder, VASurfaceID surface,
++decoder->FramesDropped;
Warning(_("video: output buffer full, dropping frame (%d/%d)\n"),
decoder->FramesDropped, decoder->FrameCounter);
if (!(decoder->FramesDisplayed % 100)) {
if (!(decoder->FramesDisplayed % 300)) {
VaapiPrintFrames(decoder);
}
if (softdec) { // software surfaces only
@@ -2031,7 +2056,7 @@ static void VaapiQueueSurface(VaapiDecoder * decoder, VASurfaceID surface,
% VIDEO_SURFACES_MAX;
atomic_inc(&decoder->SurfacesFilled);
Debug(4, "video/vaapi: yy video surface %#x ready\n", surface);
Debug(4, "video/vaapi: yy video surface %#010x ready\n", surface);
}
#if 0
@@ -2099,7 +2124,7 @@ static void VaapiBlackSurface(VaapiDecoder * decoder)
start = GetMsTicks();
}
Debug(4, "video/vaapi: yy black video surface %#x displayed\n",
Debug(4, "video/vaapi: yy black video surface %#010x displayed\n",
decoder->BlackSurface);
sync = GetMsTicks();
xcb_flush(Connection);
@@ -2239,7 +2264,7 @@ static void VaapiCpuDeinterlace(VaapiDecoder * decoder, VASurfaceID surface)
vaPutImage(decoder->VaDisplay, surface, image->image_id, 0, 0,
image->width, image->height, 0, 0, image->width,
image->height)) != VA_STATUS_SUCCESS) {
Fatal("video/vaapi: can't put image %d!\n", status);
Error("video/vaapi: can't put image %d!\n", status);
}
}
@@ -2297,7 +2322,7 @@ static void VaapiCpuDeinterlace(VaapiDecoder * decoder, VASurfaceID surface)
if (vaPutImage(VaDisplay, out1, img2->image_id, 0, 0, img2->width,
img2->height, 0, 0, img2->width,
img2->height) != VA_STATUS_SUCCESS) {
Fatal("video/vaapi: can't put image!\n");
Error("video/vaapi: can't put image!\n");
}
VaapiQueueSurface(decoder, out1, 1);
if (0 && vaSyncSurface(decoder->VaDisplay, out1) != VA_STATUS_SUCCESS) {
@@ -2308,7 +2333,7 @@ static void VaapiCpuDeinterlace(VaapiDecoder * decoder, VASurfaceID surface)
if (vaPutImage(VaDisplay, out2, img3->image_id, 0, 0, img3->width,
img3->height, 0, 0, img3->width,
img3->height) != VA_STATUS_SUCCESS) {
Fatal("video/vaapi: can't put image!\n");
Error("video/vaapi: can't put image!\n");
}
VaapiQueueSurface(decoder, out2, 1);
if (0 && vaSyncSurface(decoder->VaDisplay, out2) != VA_STATUS_SUCCESS) {
@@ -2429,6 +2454,9 @@ static void VaapiRenderFrame(VaapiDecoder * decoder,
VaapiSetup(decoder, video_ctx);
// FIXME: bad interlace like hw-part
// FIXME: aspect ratio
//
// detect interlaced input
//
@@ -2447,7 +2475,7 @@ static void VaapiRenderFrame(VaapiDecoder * decoder,
//
if (vaMapBuffer(VaDisplay, decoder->Image->buf, &va_image_data)
!= VA_STATUS_SUCCESS) {
Fatal("video/vaapi: can't map the image!\n");
Error(_("video/vaapi: can't map the image!\n"));
}
for (i = 0; (unsigned)i < decoder->Image->num_planes; ++i) {
picture->data[i] = va_image_data + decoder->Image->offsets[i];
@@ -2458,16 +2486,19 @@ static void VaapiRenderFrame(VaapiDecoder * decoder,
width, height);
if (vaUnmapBuffer(VaDisplay, decoder->Image->buf) != VA_STATUS_SUCCESS) {
Fatal("video/vaapi: can't unmap the image!\n");
Error(_("video/vaapi: can't unmap the image!\n"));
}
// get a free surface and upload the image
surface = VaapiGetSurface(decoder);
Debug(4, "video/vaapi: video surface %#010x displayed\n", surface);
Debug(4, "video/vaapi: buffer %dx%d <- %dx%d\n", decoder->Image->width,
decoder->Image->height, width, height);
// FIXME: intel didn't support put image.
if ((i = vaPutImage(VaDisplay, surface, decoder->Image->image_id, 0, 0,
width, height, 0, 0, width, height)
) != VA_STATUS_SUCCESS) {
Fatal("video/vaapi: can't put image %d!\n", i);
Error(_("video/vaapi: can't put image err:%d!\n"), i);
}
VaapiQueueSurface(decoder, surface, 1);
@@ -2559,7 +2590,7 @@ static void VaapiDisplayFrame(void)
if (surface == VA_INVALID_ID) {
printf(_("video/vaapi: invalid surface in ringbuffer\n"));
}
Debug(4, "video/vaapi: yy video surface %#x displayed\n", surface);
Debug(4, "video/vaapi: yy video surface %#010x displayed\n", surface);
#endif
start = GetMsTicks();
@@ -2633,7 +2664,7 @@ static void VaapiSyncDisplayFrame(VaapiDecoder * decoder)
decoder->FramesDuped++;
Warning(_("video: display buffer empty, duping frame (%d/%d)\n"),
decoder->FramesDuped, decoder->FrameCounter);
if (!(decoder->FramesDisplayed % 333)) {
if (!(decoder->FramesDisplayed % 300)) {
VaapiPrintFrames(decoder);
}
}
@@ -2697,7 +2728,7 @@ static void VaapiSyncRenderFrame(VaapiDecoder * decoder,
++decoder->FramesDropped;
Warning(_("video: dropping frame (%d/%d)\n"), decoder->FramesDropped,
decoder->FrameCounter);
if (!(decoder->FramesDisplayed % 100)) {
if (!(decoder->FramesDisplayed % 300)) {
VaapiPrintFrames(decoder);
}
decoder->DropNextFrame = 0;
@@ -3103,6 +3134,7 @@ static int VdpauHqScalingMax; ///< highest supported scaling level
static int VdpauTemporal; ///< temporal deinterlacer supported
static int VdpauTemporalSpatial; ///< temporal spatial deint. supported
static int VdpauInverseTelecine; ///< inverse telecine deint. supported
static int VdpauNoiseReduction; ///< noise reduction supported
static int VdpauSkipChroma; ///< skip chroma deint. supported
/// display surface ring buffer
@@ -3332,6 +3364,9 @@ static void VdpauPrintFrames(const VdpauDecoder * decoder)
Debug(3, "video/vdpau: %d missed, %d duped, %d dropped frames of %d\n",
decoder->FramesMissed, decoder->FramesDuped, decoder->FramesDropped,
decoder->FrameCounter);
#ifndef DEBUG
(void)decoder;
#endif
}
///
@@ -3343,8 +3378,8 @@ static void VdpauMixerSetup(VdpauDecoder * decoder)
{
VdpStatus status;
int i;
VdpVideoMixerFeature features[13];
VdpBool enables[13];
VdpVideoMixerFeature features[14];
VdpBool enables[14];
int feature_n;
VdpVideoMixerParameter paramaters[10];
void const *values[10];
@@ -3366,8 +3401,9 @@ static void VdpauMixerSetup(VdpauDecoder * decoder)
if (VdpauInverseTelecine) {
features[feature_n++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
}
// FIXME:
// VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION
if (VdpauNoiseReduction) {
features[feature_n++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
}
// VDP_VIDEO_MIXER_FEATURE_SHARPNESS
for (i = VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1;
i <= VdpauHqScalingMax; ++i) {
@@ -3426,6 +3462,12 @@ static void VdpauMixerSetup(VdpauDecoder * decoder)
Debug(3, "video/vdpau: inverse telecine %s\n",
enables[feature_n - 1] ? "enabled" : "disabled");
}
if (VdpauNoiseReduction) {
enables[feature_n] = VDP_FALSE;
features[feature_n++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
Debug(3, "video/vdpau: noise reduction %s\n",
enables[feature_n - 1] ? "enabled" : "disabled");
}
for (i = VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1;
i <= VdpauHqScalingMax; ++i) {
enables[feature_n] =
@@ -3440,6 +3482,11 @@ static void VdpauMixerSetup(VdpauDecoder * decoder)
/*
FIXME:
VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL
VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL
VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MIN_LUMA
VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MAX_LUMA
VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE
VdpVideoMixerSetAttributeValues(decoder->Mixer, attribute_n,
attributes, values);
*/
@@ -3862,6 +3909,16 @@ static void VideoVdpauInit(const char *display_name)
VdpauInverseTelecine = flag;
}
status =
VdpauVideoMixerQueryFeatureSupport(VdpauDevice,
VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION, &flag);
if (status != VDP_STATUS_OK) {
Error(_("video/vdpau: can't query feature '%s': %s\n"),
"noise-reduction", VdpauGetErrorString(status));
} else {
VdpauNoiseReduction = flag;
}
status =
VdpauVideoMixerQueryFeatureSupport(VdpauDevice,
VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE, &flag);
@@ -4301,7 +4358,9 @@ static void VdpauQueueSurface(VdpauDecoder * decoder, VdpVideoSurface surface,
Warning(_
("video/vdpau: output buffer full, dropping frame (%d/%d)\n"),
++decoder->FramesDropped, decoder->FrameCounter);
VdpauPrintFrames(decoder);
if (!(decoder->FramesDisplayed % 300)) {
VdpauPrintFrames(decoder);
}
// software surfaces only
if (softdec) {
VdpauReleaseSurface(decoder, surface);
@@ -4660,6 +4719,44 @@ static void VdpauMixVideo(VdpauDecoder * decoder)
decoder->SurfaceRead);
}
///
/// Create and display a black empty surface.
///
/// @param decoder VDPAU hw decoder
///
static void VdpauBlackSurface(VdpauDecoder * decoder)
{
VdpStatus status;
void *image;
void const *data[1];
uint32_t pitches[1];
VdpRect dst_rect;
Debug(3, "video/vdpau: black surface\n");
// FIXME: clear video window area
(void)decoder;
image = calloc(4, VideoWindowWidth * VideoWindowHeight);
dst_rect.x0 = 0;
dst_rect.y0 = 0;
dst_rect.x1 = dst_rect.x0 + VideoWindowWidth;
dst_rect.y1 = dst_rect.y0 + VideoWindowHeight;
data[0] = image;
pitches[0] = VideoWindowWidth * 4;
status =
VdpauOutputSurfacePutBitsNative(VdpauSurfacesRb[VdpauSurfaceIndex],
data, pitches, &dst_rect);
if (status != VDP_STATUS_OK) {
Error(_("video/vdpau: output surface put bits failed: %s\n"),
VdpauGetErrorString(status));
}
free(image);
}
///
/// Advance displayed frame.
///
@@ -4689,7 +4786,7 @@ static void VdpauAdvanceFrame(void)
Warning(_
("video: display buffer empty, duping frame (%d/%d)\n"),
decoder->FramesDuped, decoder->FrameCounter);
if (!(decoder->FramesDisplayed % 333)) {
if (!(decoder->FramesDisplayed % 300)) {
VdpauPrintFrames(decoder);
}
decoder->SurfaceField = decoder->Interlaced;
@@ -4736,7 +4833,12 @@ static void VdpauDisplayFrame(void)
// FIXME: can be more than 1 frame long shown
for (i = 0; i < VdpauDecoderN; ++i) {
VdpauDecoders[i]->FramesMissed++;
VdpauPrintFrames(VdpauDecoders[i]);
Warning(_("video: missed frame (%d/%d)\n"),
VdpauDecoders[i]->FramesMissed,
VdpauDecoders[i]->FrameCounter);
if (!(VdpauDecoders[i]->FramesDisplayed % 300)) {
VdpauPrintFrames(VdpauDecoders[i]);
}
}
}
last_time = first_time;
@@ -4755,8 +4857,8 @@ static void VdpauDisplayFrame(void)
filled = atomic_read(&decoder->SurfacesFilled);
// need 1 frame for progressive, 3 frames for interlaced
if (filled < 1 + 2 * decoder->Interlaced) {
// FIXME: render black surface
// FIXME: or rewrite MixVideo to support less surfaces
// FIXME: rewrite MixVideo to support less surfaces
VdpauBlackSurface(decoder);
continue;
}
@@ -4810,7 +4912,7 @@ static void VdpauSyncDisplayFrame(VdpauDecoder * decoder)
decoder->FramesDuped++;
Warning(_("video: display buffer empty, duping frame (%d/%d)\n"),
decoder->FramesDuped, decoder->FrameCounter);
if (!(decoder->FramesDisplayed % 333)) {
if (!(decoder->FramesDisplayed % 300)) {
VdpauPrintFrames(decoder);
}
}
@@ -4875,7 +4977,7 @@ static void VdpauSyncRenderFrame(VdpauDecoder * decoder,
++decoder->FramesDropped;
Warning(_("video: dropping frame (%d/%d)\n"), decoder->FramesDropped,
decoder->FrameCounter);
if (!(decoder->FramesDisplayed % 100)) {
if (!(decoder->FramesDisplayed % 300)) {
VdpauPrintFrames(decoder);
}
decoder->DropNextFrame = 0;
@@ -5648,6 +5750,7 @@ void VideoDisplayHandler(void)
pthread_mutex_init(&VideoLockMutex, NULL);
pthread_cond_init(&VideoWakeupCond, NULL);
pthread_create(&VideoThread, NULL, VideoDisplayHandlerThread, NULL);
pthread_setname_np(VideoThread, "softhddev video");
//pthread_detach(VideoThread);
}
}
@@ -5879,6 +5982,7 @@ struct vaapi_context *VideoGetVaapiContext(VideoHwDecoder * decoder)
return NULL;
}
#ifdef USE_VDPAU
///
/// Draw ffmpeg vdpau render state.
///
@@ -5888,7 +5992,6 @@ struct vaapi_context *VideoGetVaapiContext(VideoHwDecoder * decoder)
void VideoDrawRenderState(VideoHwDecoder * decoder,
struct vdpau_render_state *vrs)
{
#ifdef USE_VDPAU
if (VideoVdpauEnabled) {
VdpStatus status;
uint32_t start;
@@ -5906,17 +6009,18 @@ void VideoDrawRenderState(VideoHwDecoder * decoder,
VdpauGetErrorString(status));
}
if (end - start > 35) {
Debug(3, "video/vdpau: decoder render too slow %u ms\n",
// report this
Info(_("video/vdpau: decoder render too slow %u ms\n"),
end - start);
}
return;
}
#endif
(void)decoder;
(void)vrs;
Error(_("video/vdpau: draw render state, without vdpau enabled\n"));
return;
}
#endif
#ifndef USE_VIDEO_THREAD