mirror of
https://projects.vdr-developer.org/git/vdr-plugin-softhddevice.git
synced 2023-10-10 19:16:51 +02:00
Add audio compatibility with >=ffmpeg 1.1.
This commit is contained in:
parent
04286fb2ad
commit
f9998e7664
@ -1,6 +1,7 @@
|
||||
User johns
|
||||
Date:
|
||||
|
||||
Add compatibility with >=ffmpeg 1.1.
|
||||
Adds PIP (Picture-in-Picture) support.
|
||||
Split mpeg packets in receiver thread.
|
||||
|
||||
|
8
Makefile
8
Makefile
@ -24,6 +24,8 @@ CONFIG += -DUSE_PIP # too experimental PIP support
|
||||
#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
|
||||
# use ffmpeg libswresample
|
||||
CONFIG += $(shell pkg-config --exists libswresample && echo "-DUSE_SWRESAMPLE")
|
||||
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")
|
||||
@ -73,8 +75,8 @@ _CFLAGS = $(DEFINES) $(INCLUDES) \
|
||||
`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_SWRESAMPLE,$(CONFIG)), \
|
||||
$(shell pkg-config --cflags libswresample)) \
|
||||
$(if $(findstring USE_VAAPI,$(CONFIG)), \
|
||||
`pkg-config --cflags libva-x11 libva-glx libva`) \
|
||||
$(if $(findstring USE_ALSA,$(CONFIG)), \
|
||||
@ -89,6 +91,8 @@ LIBS += -lrt \
|
||||
`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` \
|
||||
$(if $(findstring USE_SWRESAMPLE,$(CONFIG)), \
|
||||
$(shell pkg-config --libs libswresample)) \
|
||||
$(if $(findstring USE_VDPAU,$(CONFIG)), \
|
||||
`pkg-config --libs vdpau`) \
|
||||
$(if $(findstring USE_VAAPI,$(CONFIG)), \
|
||||
|
2
Todo
2
Todo
@ -117,6 +117,8 @@ setup:
|
||||
unsorted:
|
||||
stoping vdr while plugin is suspended opens and closes a window.
|
||||
svdrp prim: support plugin names for device numbers.
|
||||
hangup PipVideoStream -> Vdpau_get_format -> xcb -> poll
|
||||
+ lock DecoderLockMutex
|
||||
|
||||
future features (not planed for 1.0 - 1.5)
|
||||
|
||||
|
337
codec.c
337
codec.c
@ -36,6 +36,8 @@
|
||||
#define USE_AUDIO_DRIFT_CORRECTION
|
||||
/// compile AC3 audio drift correction support (experimental)
|
||||
#define USE_AC3_DRIFT_CORRECTION
|
||||
/// use ffmpeg libswresample API
|
||||
#define noUSE_SWRESAMPLE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
@ -58,6 +60,9 @@
|
||||
#ifdef USE_VDPAU
|
||||
#include <libavcodec/vdpau.h>
|
||||
#endif
|
||||
#ifdef USE_SWRESAMPLE
|
||||
#include <libswresample/swresample.h>
|
||||
#endif
|
||||
|
||||
#ifndef __USE_GNU
|
||||
#define __USE_GNU
|
||||
@ -629,7 +634,16 @@ struct _audio_decoder_
|
||||
int HwSampleRate; ///< hw sample rate
|
||||
int HwChannels; ///< hw channels
|
||||
|
||||
#ifndef USE_SWRESAMPLE
|
||||
ReSampleContext *ReSample; ///< audio resampling context
|
||||
#endif
|
||||
#ifdef USE_SWRESAMPLE
|
||||
#if LIBSWRESAMPLE_VERSION_INT < AV_VERSION_INT(0, 15, 100)
|
||||
struct SwrContext *Resample; ///< audio software resample context
|
||||
#else
|
||||
SwrContext *Resample; ///< audio software resample context
|
||||
#endif
|
||||
#endif
|
||||
|
||||
int64_t LastDelay; ///< last delay
|
||||
struct timespec LastTime; ///< last time
|
||||
@ -639,6 +653,7 @@ struct _audio_decoder_
|
||||
int DriftCorr; ///< audio drift correction value
|
||||
int DriftFrac; ///< audio drift fraction for ac3
|
||||
|
||||
#ifndef USE_SWRESAMPLE
|
||||
struct AVResampleContext *AvResample; ///< second audio resample context
|
||||
#define MAX_CHANNELS 8 ///< max number of channels supported
|
||||
int16_t *Buffer[MAX_CHANNELS]; ///< deinterleave sample buffers
|
||||
@ -646,9 +661,12 @@ struct _audio_decoder_
|
||||
int16_t *Remain[MAX_CHANNELS]; ///< filter remaining samples
|
||||
int RemainSize; ///< size of remain buffer
|
||||
int RemainCount; ///< number of remaining samples
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef USE_AUDIO_DRIFT_CORRECTION
|
||||
#define CORRECT_PCM 1 ///< do PCM audio-drift correction
|
||||
#define CORRECT_AC3 2 ///< do AC3§ audio-drift correction
|
||||
static char CodecAudioDrift; ///< flag: enable audio-drift correction
|
||||
#else
|
||||
static const int CodecAudioDrift = 0;
|
||||
@ -718,10 +736,16 @@ void CodecAudioOpen(AudioDecoder * audio_decoder, const char *name,
|
||||
}
|
||||
|
||||
if (CodecDownmix) {
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53,61,100)
|
||||
audio_decoder->AudioCtx->request_channels = 2;
|
||||
#endif
|
||||
audio_decoder->AudioCtx->request_channel_layout =
|
||||
AV_CH_LAYOUT_STEREO_DOWNMIX;
|
||||
}
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,61,100)
|
||||
// this has no effect
|
||||
// audio_decoder->AudioCtx->request_sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
#endif
|
||||
pthread_mutex_lock(&CodecLockMutex);
|
||||
// open codec
|
||||
#if LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(53,5,0)
|
||||
@ -768,6 +792,7 @@ void CodecAudioOpen(AudioDecoder * audio_decoder, const char *name,
|
||||
void CodecAudioClose(AudioDecoder * audio_decoder)
|
||||
{
|
||||
// FIXME: output any buffered data
|
||||
#ifndef USE_SWRESAMPLE
|
||||
if (audio_decoder->AvResample) {
|
||||
int ch;
|
||||
|
||||
@ -787,6 +812,12 @@ void CodecAudioClose(AudioDecoder * audio_decoder)
|
||||
audio_resample_close(audio_decoder->ReSample);
|
||||
audio_decoder->ReSample = NULL;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWRESAMPLE
|
||||
if (audio_decoder->Resample) {
|
||||
swr_free(&audio_decoder->Resample);
|
||||
}
|
||||
#endif
|
||||
if (audio_decoder->AudioCtx) {
|
||||
pthread_mutex_lock(&CodecLockMutex);
|
||||
avcodec_close(audio_decoder->AudioCtx);
|
||||
@ -895,6 +926,8 @@ static void CodecReorderAudioFrame(int16_t * buf, int size, int channels)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef USE_SWRESAMPLE
|
||||
|
||||
/**
|
||||
** Set/update audio pts clock.
|
||||
**
|
||||
@ -916,14 +949,15 @@ static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts)
|
||||
if (!delay) {
|
||||
return;
|
||||
}
|
||||
clock_gettime(CLOCK_REALTIME, &nowtime);
|
||||
clock_gettime(CLOCK_MONOTONIC, &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 %" PRId64 "ms\n", delay / 90);
|
||||
Debug(3, "codec/audio: inital drift delay %" PRId64 "ms\n",
|
||||
delay / 90);
|
||||
return;
|
||||
}
|
||||
// collect over some time
|
||||
@ -1205,14 +1239,6 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
|
||||
}
|
||||
Error(_("codec: error more than one frame data\n"));
|
||||
}
|
||||
#ifdef notyetFF_API_OLD_DECODE_AUDIO
|
||||
// FIXME: ffmpeg git comeing
|
||||
int got_frame;
|
||||
|
||||
avcodec_decode_audio4(audio_ctx, frame, &got_frame, avpkt);
|
||||
#else
|
||||
#endif
|
||||
|
||||
// update audio clock
|
||||
if (avpkt->pts != (int64_t) AV_NOPTS_VALUE) {
|
||||
CodecAudioSetClock(audio_decoder, avpkt->pts);
|
||||
@ -1352,6 +1378,297 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_SWRESAMPLE
|
||||
|
||||
/**
|
||||
** 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_MONOTONIC, &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 drift delay %" PRId64 "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:%5" PRId64 "ms T:%5" PRId64 "ms D:%4"
|
||||
PRId64 "ms %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;
|
||||
#ifdef DEBUG
|
||||
corr = 0; // keep gcc happy
|
||||
#endif
|
||||
} else {
|
||||
|
||||
drift += audio_decoder->Drift;
|
||||
audio_decoder->Drift = drift;
|
||||
corr = (10 * audio_decoder->HwSampleRate * drift) / (90 * 1000);
|
||||
// SPDIF/HDMI passthrough
|
||||
if ((CodecAudioDrift & 2) && (!CodecPassthroughAC3
|
||||
|| audio_decoder->AudioCtx->codec_id != CODEC_ID_AC3)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_decoder->Resample && 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);
|
||||
}
|
||||
if (swr_set_compensation(audio_decoder->Resample,
|
||||
audio_decoder->DriftCorr / 10, distance)) {
|
||||
Debug(3, "codec/audio: swr_set_compensation failed\n");
|
||||
}
|
||||
}
|
||||
if (1) {
|
||||
static int c;
|
||||
|
||||
if (!(c++ % 10)) {
|
||||
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;
|
||||
|
||||
audio_ctx = audio_decoder->AudioCtx;
|
||||
Debug(3, "codec/audio: format change %s %dHz *%d channels %s\n",
|
||||
av_get_sample_fmt_name(audio_ctx->sample_fmt), 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: audio setup error\n");
|
||||
// FIXME: handle errors
|
||||
audio_decoder->HwChannels = 0;
|
||||
audio_decoder->HwSampleRate = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAC3) { // no AC3 conversion allowed
|
||||
return;
|
||||
}
|
||||
|
||||
Debug(3, "codec/audio: resample %s %dHz *%d -> %s %dHz *%d\n",
|
||||
av_get_sample_fmt_name(audio_ctx->sample_fmt), audio_ctx->sample_rate,
|
||||
audio_ctx->channels, av_get_sample_fmt_name(AV_SAMPLE_FMT_S16),
|
||||
audio_decoder->HwSampleRate, audio_decoder->HwChannels);
|
||||
|
||||
audio_decoder->Resample =
|
||||
swr_alloc_set_opts(audio_decoder->Resample, audio_ctx->channel_layout,
|
||||
AV_SAMPLE_FMT_S16, audio_decoder->HwSampleRate,
|
||||
audio_ctx->channel_layout, audio_ctx->sample_fmt,
|
||||
audio_ctx->sample_rate, 0, NULL);
|
||||
if (audio_decoder->Resample) {
|
||||
swr_init(audio_decoder->Resample);
|
||||
} else {
|
||||
Error(_("codec/audio: can't setup resample\n"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Decode an audio packet.
|
||||
**
|
||||
** PTS must be handled self.
|
||||
**
|
||||
** @note the caller has not aligned avpkt and not cleared the end.
|
||||
**
|
||||
** @param audio_decoder audio decoder data
|
||||
** @param avpkt audio packet
|
||||
*/
|
||||
void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
|
||||
{
|
||||
AVCodecContext *audio_ctx;
|
||||
AVFrame frame;
|
||||
int got_frame;
|
||||
int n;
|
||||
|
||||
audio_ctx = audio_decoder->AudioCtx;
|
||||
|
||||
frame.data[0] = NULL;
|
||||
n = avcodec_decode_audio4(audio_ctx, &frame, &got_frame,
|
||||
(AVPacket *) avpkt);
|
||||
if (n != avpkt->size) {
|
||||
if (n == AVERROR(EAGAIN)) {
|
||||
Error(_("codec/audio: latm\n"));
|
||||
return;
|
||||
}
|
||||
if (n < 0) { // no audio frame could be decompressed
|
||||
Error(_("codec/audio: bad audio frame\n"));
|
||||
return;
|
||||
}
|
||||
Error(_("codec/audio: error more than one frame data\n"));
|
||||
}
|
||||
if (!got_frame) {
|
||||
Error(_("codec/audio: no frame\n"));
|
||||
return;
|
||||
}
|
||||
// update audio clock
|
||||
if (avpkt->pts != (int64_t) AV_NOPTS_VALUE) {
|
||||
CodecAudioSetClock(audio_decoder, avpkt->pts);
|
||||
}
|
||||
// format change
|
||||
if (audio_decoder->PassthroughAC3 != CodecPassthroughAC3
|
||||
|| audio_decoder->SampleRate != audio_ctx->sample_rate
|
||||
|| audio_decoder->Channels != audio_ctx->channels) {
|
||||
CodecAudioUpdateFormat(audio_decoder);
|
||||
|
||||
}
|
||||
|
||||
if (!audio_decoder->HwSampleRate || !audio_decoder->HwChannels) {
|
||||
return; // unsupported sample format
|
||||
}
|
||||
#ifdef USE_PASSTHROUGH
|
||||
// SPDIF/HDMI passthrough
|
||||
if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
|
||||
int16_t spdif[6144 / 2];
|
||||
int spdif_sz;
|
||||
|
||||
// build SPDIF header and append A52 audio to it
|
||||
// avpkt is the original data
|
||||
spdif_sz = 6144;
|
||||
if (spdif_sz < avpkt->size + 8) {
|
||||
Error(_("codec/audio: decoded data smaller than encoded\n"));
|
||||
return;
|
||||
}
|
||||
// copy original data for output
|
||||
spdif[0] = htole16(0xF872); // iec 61937 sync word
|
||||
spdif[1] = htole16(0x4E1F);
|
||||
spdif[2] = htole16(0x01 | (avpkt->data[5] & 0x07) << 8);
|
||||
spdif[3] = htole16(avpkt->size * 8);
|
||||
// FIXME: not 100% sure, if endian is correct on not intel hardware
|
||||
swab(avpkt->data, spdif + 4, avpkt->size);
|
||||
memset(spdif + 4 + avpkt->size / 2, 0, spdif_sz - 8 - avpkt->size);
|
||||
// don't play with the ac-3 samples
|
||||
AudioEnqueue(spdif, spdif_sz);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (0) {
|
||||
char strbuf[32];
|
||||
int data_sz;
|
||||
int plane_sz;
|
||||
|
||||
data_sz =
|
||||
av_samples_get_buffer_size(&plane_sz, audio_ctx->channels,
|
||||
frame.nb_samples, audio_ctx->sample_fmt, 1);
|
||||
fprintf(stderr, "codec/audio: sample_fmt %s\n",
|
||||
av_get_sample_fmt_name(audio_ctx->sample_fmt));
|
||||
av_get_channel_layout_string(strbuf, 32, audio_ctx->channels,
|
||||
audio_ctx->channel_layout);
|
||||
fprintf(stderr, "codec/audio: layout %s\n", strbuf);
|
||||
fprintf(stderr,
|
||||
"codec/audio: channels %d samples %d plane %d data %d\n",
|
||||
audio_ctx->channels, frame.nb_samples, plane_sz, data_sz);
|
||||
}
|
||||
|
||||
if (audio_decoder->Resample) {
|
||||
uint8_t outbuf[8192 * 2 * 8];
|
||||
uint8_t *out[1];
|
||||
|
||||
out[0] = outbuf;
|
||||
n = swr_convert(audio_decoder->Resample, out,
|
||||
sizeof(outbuf) / (2 * audio_decoder->HwChannels),
|
||||
(const uint8_t **)frame.extended_data, frame.nb_samples);
|
||||
if (n > 0) {
|
||||
CodecReorderAudioFrame((int16_t *) outbuf,
|
||||
n * 2 * audio_decoder->HwChannels, audio_decoder->HwChannels);
|
||||
AudioEnqueue(outbuf, n * 2 * audio_decoder->HwChannels);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
** Flush the audio decoder.
|
||||
**
|
||||
|
Loading…
Reference in New Issue
Block a user