summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Reufer <thomas@reufer.ch>2014-07-13 20:47:02 +0200
committerThomas Reufer <thomas@reufer.ch>2014-07-13 20:47:02 +0200
commit405aa1be9ab60dba1a3f3544b2cb5b66fb02f9c7 (patch)
treeb7ecfbd4eb8de54a14ee77e78c822ad13ff47f91
parentf6f58bc01c476fecfe6edaf0f51feec7b810402c (diff)
downloadvdr-plugin-rpihddevice-405aa1be9ab60dba1a3f3544b2cb5b66fb02f9c7.tar.gz
vdr-plugin-rpihddevice-405aa1be9ab60dba1a3f3544b2cb5b66fb02f9c7.tar.bz2
support for >=ffmpeg-1.2 and >=libav-0.8 with resampling
-rw-r--r--HISTORY2
-rw-r--r--Makefile36
-rw-r--r--audio.c183
-rw-r--r--omx.c2
4 files changed, 176 insertions, 47 deletions
diff --git a/HISTORY b/HISTORY
index 410f00e..ccd6b1e 100644
--- a/HISTORY
+++ b/HISTORY
@@ -2,6 +2,8 @@ VDR Plugin 'rpihddevice' Revision History
-----------------------------------------
- new:
+ - support building against external ffmpeg/libav by setting EXT_LIBAV
+ - support for >=ffmpeg-1.2 and >=libav-0.8 with resampling
- setup option for video framing mode in case of incompatible aspect ratio
- redirect ffmpeg messages to plugin/VDR logs
- added cppcheck as Makefile target (suggested by Rolf Ahrenberg)
diff --git a/Makefile b/Makefile
index 8a991d5..0e5ad3a 100644
--- a/Makefile
+++ b/Makefile
@@ -57,16 +57,40 @@ ILCDIR =ilclient
VCINCDIR =$(SDKSTAGE)/opt/vc/include
VCLIBDIR =$(SDKSTAGE)/opt/vc/lib
-INCLUDES += -I$(ILCDIR) -I$(VCINCDIR) -I$(VCINCDIR)/interface/vcos/pthreads -I$(VCINCDIR)/interface/vmcs_host/linux
-
-LDFLAGS += -L$(VCLIBDIR) -lbcm_host -lvcos -lvchiq_arm -lopenmaxil -lGLESv2 -lEGL -lpthread -lrt -lavcodec -lavformat
-LDFLAGS += -Wl,--whole-archive $(ILCDIR)/libilclient.a -Wl,--no-whole-archive
+INCLUDES += -I$(ILCDIR) -I$(VCINCDIR) -I$(VCINCDIR)/interface/vcos/pthreads
+INCLUDES += -I$(VCINCDIR)/interface/vmcs_host/linux
+
+LDLIBS += -lbcm_host -lvcos -lvchiq_arm -lopenmaxil -lGLESv2 -lEGL -lpthread -lrt
+LDLIBS += -Wl,--whole-archive $(ILCDIR)/libilclient.a -Wl,--no-whole-archive
+LDFLAGS += -L$(VCLIBDIR)
DEBUG ?= 0
ifeq ($(DEBUG), 1)
DEFINES += -DDEBUG
endif
-
+
+# ffmpeg/libav configuration
+ifdef EXT_LIBAV
+ LIBAV_PKGCFG = $(shell PKG_CONFIG_PATH=$(EXT_LIBAV)/lib/pkgconfig pkg-config $(1))
+else
+ LIBAV_PKGCFG = $(shell pkg-config $(1))
+endif
+
+LDLIBS += $(call LIBAV_PKGCFG,--libs libavcodec) $(call LIBAV_PKGCFG,--libs libavformat)
+INCLUDES += $(call LIBAV_PKGCFG,--cflags libavcodec) $(call LIBAV_PKGCFG,--cflags libavformat)
+
+ifeq ($(call LIBAV_PKGCFG,--exists libswresample && echo 1), 1)
+ DEFINES += -DHAVE_LIBSWRESAMPLE
+ LDLIBS += $(call LIBAV_PKGCFG,--libs libswresample)
+ INCLUDES += $(call LIBAV_PKGCFG,--cflags libswresample)
+else
+ifeq ($(call LIBAV_PKGCFG,--exists libavresample && echo 1), 1)
+ DEFINES += -DHAVE_LIBAVRESAMPLE
+ LDLIBS += $(call LIBAV_PKGCFG,--libs libavresample)
+ INCLUDES += $(call LIBAV_PKGCFG,--cflags libavresample)
+endif
+endif
+
### The object files (add further files here):
ILCLIENT = $(ILCDIR)/libilclient.a
@@ -119,7 +143,7 @@ install-i18n: $(I18Nmsgs)
### Targets:
$(SOFILE): $(ILCLIENT) $(OBJS)
- $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LDLIBS) -o $@
$(ILCLIENT):
$(MAKE) --no-print-directory -C $(ILCDIR) all
diff --git a/audio.c b/audio.c
index 545c298..f7b2513 100644
--- a/audio.c
+++ b/audio.c
@@ -14,6 +14,46 @@
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/log.h>
+#include <libavutil/opt.h>
+
+// ffmpeg's resampling
+#ifdef HAVE_LIBSWRESAMPLE
+# include <libswresample/swresample.h>
+# define DO_RESAMPLE
+#endif
+
+// libav's resampling
+#ifdef HAVE_LIBAVRESAMPLE
+# include <libavresample/avresample.h>
+# include <libavutil/samplefmt.h>
+# define DO_RESAMPLE
+# define SwrContext AVAudioResampleContext
+# define swr_alloc avresample_alloc_context
+# define swr_init avresample_open
+# define swr_free avresample_free
+# define swr_convert(ctx, dst, out_cnt, src, in_cnt) \
+ avresample_convert(ctx, dst, 0, out_cnt, (uint8_t**)src, 0, in_cnt)
+#endif
+
+// legacy libavcodec
+#if LIBAVCODEC_VERSION_MAJOR < 55
+# define av_frame_alloc avcodec_alloc_frame
+# define av_frame_free avcodec_free_frame
+# define av_frame_unref avcodec_get_frame_defaults
+# define AV_CODEC_ID_MP3 CODEC_ID_MP3
+# define AV_CODEC_ID_AC3 CODEC_ID_AC3
+# define AV_CODEC_ID_EAC3 CODEC_ID_EAC3
+# define AV_CODEC_ID_AAC CODEC_ID_AAC
+#endif
+
+#if LIBAVCODEC_VERSION_MAJOR < 54
+# define avcodec_free_frame av_free
+#endif
+
+// prevent depreciated warnings for >ffmpeg-1.2.x and >libav-9.x
+#if LIBAVCODEC_VERSION_MAJOR > 54
+# undef FF_API_REQUEST_CHANNELS
+#endif
}
#include <queue>
@@ -689,6 +729,24 @@ const uint16_t cRpiAudioDecoder::cParser::Ac3FrameSizeTable[38][3] =
/* ------------------------------------------------------------------------- */
+#define AV_CH_LAYOUT(ch) ( \
+ ch == 1 ? AV_CH_LAYOUT_MONO : \
+ ch == 2 ? AV_CH_LAYOUT_STEREO : \
+ ch == 3 ? AV_CH_LAYOUT_2POINT1 : \
+ ch == 6 ? AV_CH_LAYOUT_5POINT1 : 0)
+
+#define AV_SAMPLE_STR(fmt) ( \
+ fmt == AV_SAMPLE_FMT_U8 ? "U8" : \
+ fmt == AV_SAMPLE_FMT_S16 ? "S16" : \
+ fmt == AV_SAMPLE_FMT_S32 ? "S32" : \
+ fmt == AV_SAMPLE_FMT_FLT ? "float" : \
+ fmt == AV_SAMPLE_FMT_DBL ? "double" : \
+ fmt == AV_SAMPLE_FMT_U8P ? "U8, planar" : \
+ fmt == AV_SAMPLE_FMT_S16P ? "S16, planar" : \
+ fmt == AV_SAMPLE_FMT_S32P ? "S32, planar" : \
+ fmt == AV_SAMPLE_FMT_FLTP ? "float, planar" : \
+ fmt == AV_SAMPLE_FMT_DBLP ? "double, planar" : "unknown")
+
cRpiAudioDecoder::cRpiAudioDecoder(cOmx *omx) :
cThread(),
m_passthrough(false),
@@ -723,10 +781,10 @@ int cRpiAudioDecoder::Init(void)
av_log_set_callback(&Log);
m_codecs[cAudioCodec::ePCM ].codec = NULL;
- m_codecs[cAudioCodec::eMPG ].codec = avcodec_find_decoder(CODEC_ID_MP3);
- m_codecs[cAudioCodec::eAC3 ].codec = avcodec_find_decoder(CODEC_ID_AC3);
- m_codecs[cAudioCodec::eEAC3].codec = avcodec_find_decoder(CODEC_ID_EAC3);
- m_codecs[cAudioCodec::eAAC ].codec = avcodec_find_decoder(CODEC_ID_AAC);
+ m_codecs[cAudioCodec::eMPG ].codec = avcodec_find_decoder(AV_CODEC_ID_MP3);
+ m_codecs[cAudioCodec::eAC3 ].codec = avcodec_find_decoder(AV_CODEC_ID_AC3);
+ m_codecs[cAudioCodec::eEAC3].codec = avcodec_find_decoder(AV_CODEC_ID_EAC3);
+ m_codecs[cAudioCodec::eAAC ].codec = avcodec_find_decoder(AV_CODEC_ID_AAC);
for (int i = 0; i < cAudioCodec::eNumCodecs; i++)
{
@@ -749,13 +807,14 @@ int cRpiAudioDecoder::Init(void)
}
}
- if (ret < 0)
+ if (!ret)
+ {
+ cRpiSetup::SetAudioSetupChangedCallback(&OnAudioSetupChanged, this);
+ Start();
+ }
+ else
DeInit();
- cRpiSetup::SetAudioSetupChangedCallback(&OnAudioSetupChanged, this);
-
- Start();
-
return ret;
}
@@ -776,10 +835,7 @@ int cRpiAudioDecoder::DeInit(void)
{
cAudioCodec::eCodec codec = static_cast<cAudioCodec::eCodec>(i);
if (m_codecs[codec].codec)
- {
avcodec_close(m_codecs[codec].context);
- av_free(m_codecs[codec].context);
- }
}
av_log_set_callback(&av_log_default_callback);
@@ -833,10 +889,15 @@ void cRpiAudioDecoder::Action(void)
unsigned int outputChannels = 0;
unsigned int samplingRate = 0;
+#ifdef DO_RESAMPLE
+ AVSampleFormat sampleFormat = AV_SAMPLE_FMT_NONE;
+ SwrContext *resample = 0;
+#endif
+
cAudioCodec::eCodec codec = cAudioCodec::eInvalid;
OMX_BUFFERHEADERTYPE *buf = 0;
- AVFrame *frame = avcodec_alloc_frame();
+ AVFrame *frame = av_frame_alloc();
if (!frame)
{
ELOG("failed to allocate audio frame!");
@@ -861,8 +922,11 @@ void cRpiAudioDecoder::Action(void)
outputChannels = channels;
SetCodec(codec, outputChannels, samplingRate);
- avcodec_get_frame_defaults(frame);
+ av_frame_unref(frame);
m_setupChanged = false;
+#ifdef DO_RESAMPLE
+ sampleFormat = AV_SAMPLE_FMT_NONE;
+#endif
if (codec == cAudioCodec::eInvalid)
m_reset = true;
@@ -907,10 +971,45 @@ void cRpiAudioDecoder::Action(void)
// -- get length --
int len = m_passthrough ? m_parser->Packet()->size :
- av_samples_get_buffer_size(NULL,
- outputChannels == 6 ? 8 : outputChannels, frame->nb_samples,
- m_codecs[codec].context->sample_fmt, 1);
+ av_samples_get_buffer_size(NULL, outputChannels,
+ frame->nb_samples, AV_SAMPLE_FMT_S16, 1);
+#ifdef DO_RESAMPLE
+ if (sampleFormat != m_codecs[codec].context->sample_fmt)
+ {
+ sampleFormat = m_codecs[codec].context->sample_fmt;
+ swr_free(&resample);
+ resample = swr_alloc();
+
+ if (!resample)
+ {
+ ELOG("failed to allocate resampling context!");
+ m_reset = true;
+ break;
+ }
+
+ DBG("decoding %s samples in %d channels",
+ AV_SAMPLE_STR(sampleFormat),
+ m_codecs[codec].context->channels);
+
+ av_opt_set_int(resample, "in_channel_layout",
+ AV_CH_LAYOUT(m_codecs[codec].context->channels), 0);
+ av_opt_set_int(resample, "out_channel_layout",
+ AV_CH_LAYOUT(outputChannels), 0);
+
+ av_opt_set_int(resample, "in_sample_rate",
+ m_codecs[codec].context->sample_rate, 0);
+ av_opt_set_int(resample, "out_sample_rate",
+ m_codecs[codec].context->sample_rate, 0);
+
+ av_opt_set_int(resample, "in_sample_fmt",
+ m_codecs[codec].context->sample_fmt, 0);
+ av_opt_set_int(resample, "out_sample_fmt",
+ AV_SAMPLE_FMT_S16, 0);
+
+ swr_init(resample);
+ }
+#endif
if (len > (signed)(buf->nAllocLen - buf->nFilledLen) || len < 0)
{
// rise reset flag if packet is even bigger than allocated buffer
@@ -932,25 +1031,27 @@ void cRpiAudioDecoder::Action(void)
}
else if (frame->nb_samples)
{
- if (outputChannels == 6)
+#ifdef DO_RESAMPLE
+ uint8_t *dst[] = { buf->pBuffer + buf->nFilledLen };
+ const uint8_t** src = (const uint8_t **)(frame->extended_data);
+
+ if (swr_convert(resample,
+ dst, frame->nb_samples, src, frame->nb_samples) > 0)
{
- int32_t* src = (int32_t*)frame->data[0];
- int32_t* dst = (int32_t*)(buf->pBuffer + buf->nFilledLen);
-
- // interleaved copy to fit 5.1 data into 8 channels
- for (int i = 0; i < frame->nb_samples; i++)
- {
- *dst++ = *src++; // LF & RF
- *dst++ = *src++; // CF & LFE
- *dst++ = *src++; // LR & RR
- *dst++ = 0; // empty channels
- }
+ buf->nFilledLen += len;
+ av_frame_unref(frame);
}
else
- memcpy(buf->pBuffer + buf->nFilledLen, frame->data[0], len);
-
+ {
+ m_reset = true;
+ ELOG("failed to resample audio frame!");
+ break;
+ }
+#else
+ memcpy(buf->pBuffer + buf->nFilledLen, frame->data[0], len);
buf->nFilledLen += len;
- avcodec_get_frame_defaults(frame);
+ av_frame_unref(frame);
+#endif
}
// if there's a valid PTS after shrinking, a complete PES packet
// has been handled and is ready to play
@@ -962,7 +1063,7 @@ void cRpiAudioDecoder::Action(void)
if (m_reset)
{
m_parser->Reset();
- avcodec_get_frame_defaults(frame);
+ av_frame_unref(frame);
if (buf)
{
cOmx::PtsToTicks(0, buf->nTimeStamp);
@@ -982,7 +1083,11 @@ void cRpiAudioDecoder::Action(void)
m_wait->Wait(50);
}
- av_free(frame);
+#ifdef DO_RESAMPLE
+ swr_free(&resample);
+#endif
+
+ av_frame_free(&frame);
SetCodec(cAudioCodec::eInvalid, outputChannels, samplingRate);
DLOG("cAudioDecoder() thread ended");
}
@@ -996,9 +1101,6 @@ void cRpiAudioDecoder::SetCodec(cAudioCodec::eCodec codec, unsigned int &channel
DLOG("set audio codec to %dch %s", channels, cAudioCodec::Str(codec));
avcodec_flush_buffers(m_codecs[codec].context);
- m_codecs[codec].context->request_channel_layout = AV_CH_LAYOUT_NATIVE;
- m_codecs[codec].context->request_channels = 0;
-
m_passthrough = false;
cAudioCodec::eCodec outputFormat = cAudioCodec::ePCM;
cRpiAudioPort::ePort outputPort = cRpiAudioPort::eLocal;
@@ -1017,16 +1119,17 @@ void cRpiAudioDecoder::SetCodec(cAudioCodec::eCodec codec, unsigned int &channel
}
else
{
- m_codecs[codec].context->request_channel_layout = AV_CH_LAYOUT_STEREO_DOWNMIX;
- m_codecs[codec].context->request_channels = 2;
channels = 2;
-
// if 2ch PCM audio on HDMI is supported
if (cRpiSetup::GetAudioPort() == cRpiAudioPort::eHDMI &&
cRpiSetup::IsAudioFormatSupported(cAudioCodec::ePCM, 2, samplingRate))
outputPort = cRpiAudioPort::eHDMI;
}
+ m_codecs[codec].context->request_channel_layout = AV_CH_LAYOUT(channels);
+#if FF_API_REQUEST_CHANNELS
+ m_codecs[codec].context->request_channels = channels;
+#endif
m_omx->SetupAudioRender(outputFormat, channels, outputPort, samplingRate);
ILOG("set %s audio output format to %dch %s, %d.%dkHz%s",
cRpiAudioPort::Str(outputPort), channels, cAudioCodec::Str(outputFormat),
diff --git a/omx.c b/omx.c
index ccfeec8..75a7a8a 100644
--- a/omx.c
+++ b/omx.c
@@ -1001,7 +1001,7 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels,
OMX_AUDIO_PARAM_PCMMODETYPE pcm;
OMX_INIT_STRUCT(pcm);
pcm.nPortIndex = 100;
- pcm.nChannels = channels == 6 ? 8 : channels;
+ pcm.nChannels = channels;
pcm.eNumData = OMX_NumericalDataSigned;
pcm.eEndian = OMX_EndianLittle;
pcm.bInterleaved = OMX_TRUE;