diff --git a/Makefile.am b/Makefile.am
index 87742f49e3..9e85852d0e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,7 @@ dist_doc_DATA = doc/ffms2-api.html doc/ffms2-changelog.html doc/style.css
INCLUDES = -I. -I$(top_srcdir)/include -I$(top_srcdir)/src/config @LIBAV_CFLAGS@ @ZLIB_CPPFLAGS@ -include config.h
lib_LTLIBRARIES = src/core/libffms2.la
-src_core_libffms2_la_LIBADD = @LIBAV_LIBS@ @ZLIB_LDFLAGS@ -lz @LTUNDEF@
+src_core_libffms2_la_LIBADD = @LIBAV_LIBS@ @AVRESAMPLE_LIBS@ @ZLIB_LDFLAGS@ -lz @LTUNDEF@
src_core_libffms2_la_SOURCES = \
src/core/audiosource.h \
src/core/audiosource.cpp \
@@ -39,17 +39,17 @@ src_core_libffms2_la_SOURCES = \
src/core/utils.cpp \
src/core/videosource.h \
src/core/videosource.cpp \
- src/core/videoutils.h \
- src/core/videoutils.cpp \
- src/core/wave64writer.h \
- src/core/wave64writer.cpp \
- src/vapoursynth/VapourSynth.h \
- src/vapoursynth/vapoursource.h \
- src/vapoursynth/vapoursource.cpp \
- src/vapoursynth/vapoursynth.cpp
-
-include_HEADERS = $(top_srcdir)/include/ffms.h $(top_srcdir)/include/ffmscompat.h
-
+ src/core/videoutils.h \
+ src/core/videoutils.cpp \
+ src/core/wave64writer.h \
+ src/core/wave64writer.cpp \
+ src/vapoursynth/VapourSynth.h \
+ src/vapoursynth/vapoursource.h \
+ src/vapoursynth/vapoursource.cpp \
+ src/vapoursynth/vapoursynth.cpp
+
+include_HEADERS = $(top_srcdir)/include/ffms.h $(top_srcdir)/include/ffmscompat.h
+
bin_PROGRAMS = src/index/ffmsindex
src_index_ffmsindex_SOURCES = src/index/ffmsindex.cpp
src_index_ffmsindex_LDADD = src/core/libffms2.la
diff --git a/Makefile.in b/Makefile.in
index 9db1b1bb7d..1b08a7ae25 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.12.5 from Makefile.am.
+# Makefile.in generated by automake 1.12.6 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2012 Free Software Foundation, Inc.
@@ -218,6 +218,8 @@ AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
+AVRESAMPLE_CFLAGS = @AVRESAMPLE_CFLAGS@
+AVRESAMPLE_LIBS = @AVRESAMPLE_LIBS@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
@@ -321,6 +323,7 @@ htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
+libavresample = @libavresample@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
@@ -347,7 +350,7 @@ pkgconfig_DATA = ffms2.pc
dist_doc_DATA = doc/ffms2-api.html doc/ffms2-changelog.html doc/style.css
INCLUDES = -I. -I$(top_srcdir)/include -I$(top_srcdir)/src/config @LIBAV_CFLAGS@ @ZLIB_CPPFLAGS@ -include config.h
lib_LTLIBRARIES = src/core/libffms2.la
-src_core_libffms2_la_LIBADD = @LIBAV_LIBS@ @ZLIB_LDFLAGS@ -lz @LTUNDEF@
+src_core_libffms2_la_LIBADD = @LIBAV_LIBS@ @AVRESAMPLE_LIBS@ @ZLIB_LDFLAGS@ -lz @LTUNDEF@
src_core_libffms2_la_SOURCES = \
src/core/audiosource.h \
src/core/audiosource.cpp \
diff --git a/aclocal.m4 b/aclocal.m4
index bc85fce595..50c5aa9093 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,4 +1,4 @@
-# generated automatically by aclocal 1.12.5 -*- Autoconf -*-
+# generated automatically by aclocal 1.12.6 -*- Autoconf -*-
# Copyright (C) 1996-2012 Free Software Foundation, Inc.
@@ -234,7 +234,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.12'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
-m4_if([$1], [1.12.5], [],
+m4_if([$1], [1.12.6], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
@@ -250,7 +250,7 @@ m4_define([_AM_AUTOCONF_VERSION], [])
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.12.5])dnl
+[AM_AUTOMAKE_VERSION([1.12.6])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
diff --git a/build-msvc/config.targets b/build-msvc/config.targets
index 814a0a599e..8d2ae26127 100644
--- a/build-msvc/config.targets
+++ b/build-msvc/config.targets
@@ -17,6 +17,10 @@
WITH_PTHREAD_GC2;%(PreprocessorDefinitions)
+
+ WITH_AVRESAMPLE;%(PreprocessorDefinitions)
+
+
FFMS_USE_FFMPEG_COMPAT;%(PreprocessorDefinitions)
diff --git a/build-msvc/config.xml b/build-msvc/config.xml
index bce93186f1..05aaa806be 100644
--- a/build-msvc/config.xml
+++ b/build-msvc/config.xml
@@ -42,4 +42,10 @@
Description="Was FFmpeg/libav built with pthreads rather than w32threads?"
/>
+
+
diff --git a/build-msvc/ffms2.vcxproj b/build-msvc/ffms2.vcxproj
index f0114744ab..8ba001d218 100644
--- a/build-msvc/ffms2.vcxproj
+++ b/build-msvc/ffms2.vcxproj
@@ -29,6 +29,7 @@
false
false
false
+ true
libav
diff --git a/config.sub b/config.sub
index 89b1286300..8df5511094 100755
--- a/config.sub
+++ b/config.sub
@@ -4,7 +4,7 @@
# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012 Free Software Foundation, Inc.
-timestamp='2012-10-10'
+timestamp='2012-12-06'
# This file is (in principle) common to ALL GNU software.
# The presence of a machine in this file suggests that SOME GNU software
@@ -1026,7 +1026,11 @@ case $basic_machine in
basic_machine=i586-unknown
os=-pw32
;;
- rdos)
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=-rdos
+ ;;
+ rdos32)
basic_machine=i386-pc
os=-rdos
;;
diff --git a/configure b/configure
index 28e6dfd032..c237a48fe4 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for ffms2 2.17.3.
+# Generated by GNU Autoconf 2.69 for ffms2 2.17.4.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -587,8 +587,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='ffms2'
PACKAGE_TARNAME='ffms2'
-PACKAGE_VERSION='2.17.3'
-PACKAGE_STRING='ffms2 2.17.3'
+PACKAGE_VERSION='2.17.4'
+PACKAGE_STRING='ffms2 2.17.4'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -634,6 +634,9 @@ am__EXEEXT_TRUE
LTLIBOBJS
LIBOBJS
LTUNDEF
+libavresample
+AVRESAMPLE_LIBS
+AVRESAMPLE_CFLAGS
LIBAV_LIBS
LIBAV_CFLAGS
pkgconfigdir
@@ -781,6 +784,7 @@ with_gnu_ld
with_sysroot
enable_libtool_lock
with_zlib
+enable_avresample
'
ac_precious_vars='build_alias
host_alias
@@ -799,7 +803,9 @@ PKG_CONFIG
PKG_CONFIG_PATH
PKG_CONFIG_LIBDIR
LIBAV_CFLAGS
-LIBAV_LIBS'
+LIBAV_LIBS
+AVRESAMPLE_CFLAGS
+AVRESAMPLE_LIBS'
# Initialize some variables set by options.
@@ -1340,7 +1346,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures ffms2 2.17.3 to adapt to many kinds of systems.
+\`configure' configures ffms2 2.17.4 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1410,7 +1416,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of ffms2 2.17.3:";;
+ short | recursive ) echo "Configuration of ffms2 2.17.4:";;
esac
cat <<\_ACEOF
@@ -1433,6 +1439,7 @@ Optional Features:
--enable-fast-install[=PKGS]
optimize for fast installation [default=yes]
--disable-libtool-lock avoid locking (might break parallel builds)
+ --enable-avresample use libavresample for audio resampling
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -1464,6 +1471,10 @@ Some influential environment variables:
LIBAV_CFLAGS
C compiler flags for LIBAV, overriding pkg-config
LIBAV_LIBS linker flags for LIBAV, overriding pkg-config
+ AVRESAMPLE_CFLAGS
+ C compiler flags for AVRESAMPLE, overriding pkg-config
+ AVRESAMPLE_LIBS
+ linker flags for AVRESAMPLE, overriding pkg-config
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
@@ -1531,7 +1542,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-ffms2 configure 2.17.3
+ffms2 configure 2.17.4
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2071,7 +2082,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by ffms2 $as_me 2.17.3, which was
+It was created by ffms2 $as_me 2.17.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2897,7 +2908,7 @@ fi
# Define the identity of the package.
PACKAGE='ffms2'
- VERSION='2.17.3'
+ VERSION='2.17.4'
cat >>confdefs.h <<_ACEOF
@@ -15987,6 +15998,108 @@ fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
+# Check whether --enable-avresample was given.
+if test "${enable_avresample+set}" = set; then :
+ enableval=$enable_avresample;
+fi
+
+if test x$enable_avresample != xno; then :
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for AVRESAMPLE" >&5
+$as_echo_n "checking for AVRESAMPLE... " >&6; }
+
+if test -n "$AVRESAMPLE_CFLAGS"; then
+ pkg_cv_AVRESAMPLE_CFLAGS="$AVRESAMPLE_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libavresample >= 1.0.0\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libavresample >= 1.0.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_AVRESAMPLE_CFLAGS=`$PKG_CONFIG --cflags "libavresample >= 1.0.0" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$AVRESAMPLE_LIBS"; then
+ pkg_cv_AVRESAMPLE_LIBS="$AVRESAMPLE_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libavresample >= 1.0.0\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libavresample >= 1.0.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_AVRESAMPLE_LIBS=`$PKG_CONFIG --libs "libavresample >= 1.0.0" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ AVRESAMPLE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libavresample >= 1.0.0" 2>&1`
+ else
+ AVRESAMPLE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libavresample >= 1.0.0" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$AVRESAMPLE_PKG_ERRORS" >&5
+
+
+ if test x$enable_avresample = xyes; then :
+ as_fn_error $? "--enable-avresample was specified, but avresample 1.0.0+ could not be found." "$LINENO" 5
+fi
+ enable_avresample=no
+
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+ if test x$enable_avresample = xyes; then :
+ as_fn_error $? "--enable-avresample was specified, but avresample 1.0.0+ could not be found." "$LINENO" 5
+fi
+ enable_avresample=no
+
+else
+ AVRESAMPLE_CFLAGS=$pkg_cv_AVRESAMPLE_CFLAGS
+ AVRESAMPLE_LIBS=$pkg_cv_AVRESAMPLE_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_avresample=yes
+fi
+
+fi
+
+if test x$enable_avresample; then :
+ libavresample="libavresample"
+
+$as_echo "#define WITH_AVRESAMPLE 1" >>confdefs.h
+
+fi
+
+
+
+
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -Wl,-Bsymbolic is needed" >&5
$as_echo_n "checking whether -Wl,-Bsymbolic is needed... " >&6; }
if test "$enable_shared" = yes; then
@@ -16621,7 +16734,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by ffms2 $as_me 2.17.3, which was
+This file was extended by ffms2 $as_me 2.17.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -16687,7 +16800,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-ffms2 config.status 2.17.3
+ffms2 config.status 2.17.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index ae1cc0b338..a1abaf09a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -181,6 +181,25 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
AC_MSG_RESULT([no])
])
+AC_ARG_ENABLE(avresample,
+ AS_HELP_STRING([--enable-avresample],
+ [use libavresample for audio resampling]))
+AS_IF([test x$enable_avresample != xno], [
+ PKG_CHECK_MODULES(AVRESAMPLE, [libavresample >= 1.0.0], [enable_avresample=yes], [
+ AS_IF([test x$enable_avresample = xyes],
+ [AC_MSG_ERROR([--enable-avresample was specified, but avresample 1.0.0+ could not be found.])])
+ enable_avresample=no
+ ])
+])
+
+AS_IF([test x$enable_avresample],
+ [libavresample="libavresample"
+ AC_DEFINE([WITH_AVRESAMPLE], [1], [Use avresample])])
+
+AC_SUBST([AVRESAMPLE_CFLAGS])
+AC_SUBST([AVRESAMPLE_LIBS])
+AC_SUBST([libavresample])
+
AC_MSG_CHECKING([whether -Wl,-Bsymbolic is needed])
if test "$enable_shared" = yes; then
_LDFLAGS="$LDFLAGS"
diff --git a/ffms2.pc.in b/ffms2.pc.in
index 7e316ead9e..2568b61423 100644
--- a/ffms2.pc.in
+++ b/ffms2.pc.in
@@ -7,7 +7,7 @@ includedir=@includedir@
Name: ffms2
Description: The Fabulous FM Library 2
-Requires.private: libavformat libavcodec libswscale libavutil
+Requires.private: libavformat libavcodec libswscale libavutil @libavresample@
Version: @FFMS_VERSION@
Libs.private: @ZLIB_LDFLAGS@ -lz
Libs: -L${libdir} -lffms2
diff --git a/include/ffms.h b/include/ffms.h
index ce6869b8c6..70aed7bb0d 100644
--- a/include/ffms.h
+++ b/include/ffms.h
@@ -22,7 +22,7 @@
#define FFMS_H
// Version format: major - minor - micro - bump
-#define FFMS_VERSION ((2 << 24) | (17 << 16) | (3 << 8) | 0)
+#define FFMS_VERSION ((2 << 24) | (17 << 16) | (4 << 8) | 0)
#include
@@ -113,6 +113,7 @@ typedef enum FFMS_Errors {
FFMS_ERROR_TRACK, // track handling
FFMS_ERROR_WAVE_WRITER, // WAVE64 file writer
FFMS_ERROR_CANCELLED, // operation aborted
+ FFMS_ERROR_RESAMPLING, // audio resampling (libavresample)
// Subtypes - what caused the error
FFMS_ERROR_UNKNOWN = 20, // unknown error
@@ -237,6 +238,53 @@ typedef enum FFMS_ColorRanges {
FFMS_CR_JPEG = 2 // 2^n-1, or "fullrange"
} FFMS_ColorRanges;
+typedef enum FFMS_MixingCoefficientType {
+ FFMS_MIXING_COEFFICIENT_Q8 = 0,
+ FFMS_MIXING_COEFFICIENT_Q15 = 1,
+ FFMS_MIXING_COEFFICIENT_FLT = 2
+} FFMS_MixingCoefficientType;
+
+typedef enum FFMS_MatrixEncoding {
+ FFMS_MATRIX_ENCODING_NONE = 0,
+ FFMS_MATRIX_ENCODING_DOBLY = 1,
+ FFMS_MATRIX_ENCODING_PRO_LOGIC_II = 2
+} FFMS_MatrixEncoding;
+
+typedef enum FFMS_ResampleFilterType {
+ FFMS_RESAMPLE_FILTER_CUBIC = 0,
+ FFMS_RESAMPLE_FILTER_SINC = 1,
+ FFMS_RESAMPLE_FILTER_KAISER = 2
+} FFMS_ResampleFilterType;
+
+typedef enum FFMS_AudioDitherMethod {
+ FFMS_RESAMPLE_DITHER_NONE = 0,
+ FFMS_RESAMPLE_DITHER_RECTANGULAR = 1,
+ FFMS_RESAMPLE_DITHER_TRIANGULAR = 2,
+ FFMS_RESAMPLE_DITHER_TRIANGULAR_HIGHPASS = 3,
+ FFMS_RESAMPLE_DITHER_TRIANGULAR_NOISESHAPING = 4
+} FFMS_AudioDitherMethod;
+
+typedef struct FFMS_ResampleOptions {
+ int64_t ChannelLayout;
+ FFMS_SampleFormat SampleFormat;
+ int SampleRate;
+ FFMS_MixingCoefficientType MixingCoefficientType;
+ double CenterMixLevel;
+ double SurroundMixLevel;
+ double LFEMixLevel;
+ int Normalize;
+ int ForceResample;
+ int ResampleFilterSize;
+ int ResamplePhaseShift;
+ int LinearInterpolation;
+ double CutoffFrequencyRatio;
+ FFMS_MatrixEncoding MatrixedStereoEncoding;
+ FFMS_ResampleFilterType FilterType;
+ int KaiserBeta;
+ FFMS_AudioDitherMethod DitherMethod;
+} FFMS_ResampleOptions;
+
+
typedef struct FFMS_Frame {
uint8_t *Data[4];
int Linesize[4];
@@ -319,6 +367,9 @@ FFMS_API(int) FFMS_SetOutputFormatV2(FFMS_VideoSource *V, const int *TargetForma
FFMS_API(void) FFMS_ResetOutputFormatV(FFMS_VideoSource *V);
FFMS_API(int) FFMS_SetInputFormatV(FFMS_VideoSource *V, int ColorSpace, int ColorRange, int Format, FFMS_ErrorInfo *ErrorInfo); /* Introduced in FFMS_VERSION ((2 << 24) | (17 << 16) | (1 << 8) | 0) */
FFMS_API(void) FFMS_ResetInputFormatV(FFMS_VideoSource *V);
+FFMS_API(FFMS_ResampleOptions *) FFMS_CreateResampleOptions(FFMS_AudioSource *A); /* Introduced in FFMS_VERSION ((2 << 24) | (15 << 16) | (4 << 8) | 0) */
+FFMS_API(int) FFMS_SetOutputFormatA(FFMS_AudioSource *A, const FFMS_ResampleOptions*options, FFMS_ErrorInfo *ErrorInfo); /* Introduced in FFMS_VERSION ((2 << 24) | (15 << 16) | (4 << 8) | 0) */
+FFMS_API(void) FFMS_DestroyResampleOptions(FFMS_ResampleOptions *options); /* Introduced in FFMS_VERSION ((2 << 24) | (15 << 16) | (4 << 8) | 0) */
FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index);
FFMS_API(int) FFMS_GetSourceType(FFMS_Index *Index);
FFMS_API(int) FFMS_GetSourceTypeI(FFMS_Indexer *Indexer);
diff --git a/src/config/config.h.in b/src/config/config.h.in
index ac67875f70..0dd3087101 100644
--- a/src/config/config.h.in
+++ b/src/config/config.h.in
@@ -90,5 +90,8 @@
/* Version number of package */
#undef VERSION
+/* Use avresample */
+#undef WITH_AVRESAMPLE
+
/* Define to `unsigned int' if does not define. */
#undef size_t
diff --git a/src/config/libs.cpp b/src/config/libs.cpp
index 752ea2f0f7..7cb011cc48 100644
--- a/src/config/libs.cpp
+++ b/src/config/libs.cpp
@@ -45,6 +45,9 @@ extern "C" {
#pragma comment(lib, "libavcodec.a")
#pragma comment(lib, "libavformat.a")
#pragma comment(lib, "libswscale.a")
+#ifdef WITH_AVRESAMPLE
+#pragma comment(lib, "libavresample.a")
+#endif
#ifdef WITH_OPENCORE_AMR_NB
#ifdef WITH_GCC_LIBAV
diff --git a/src/core/audiosource.cpp b/src/core/audiosource.cpp
index 8d7e965b1c..f839f3825e 100644
--- a/src/core/audiosource.cpp
+++ b/src/core/audiosource.cpp
@@ -23,10 +23,40 @@
#include
#include
+namespace {
+
+ int64_t ChannelLayout;
+ FFMS_SampleFormat SampleFormat;
+ int SampleRate;
+#define MAPPER(m, n) OptionMapper(n, &FFMS_ResampleOptions::m)
+OptionMapper resample_options[] = {
+ MAPPER(ChannelLayout, "out_channel_layout"),
+ MAPPER(SampleFormat, "out_sample_fmt"),
+ MAPPER(SampleRate, "out_sample_rate"),
+ MAPPER(MixingCoefficientType, "mix_coeff_type"),
+ MAPPER(CenterMixLevel, "center_mix_level"),
+ MAPPER(SurroundMixLevel, "surround_mix_level"),
+ MAPPER(LFEMixLevel, "lfe_mix_level"),
+ MAPPER(Normalize, "normalize_mix_level"),
+ MAPPER(ForceResample, "force_resampling"),
+ MAPPER(ResampleFilterSize, "filter_size"),
+ MAPPER(ResamplePhaseShift, "phase_shift"),
+ MAPPER(LinearInterpolation, "linear_interp"),
+ MAPPER(CutoffFrequencyRatio, "cutoff"),
+ MAPPER(MatrixedStereoEncoding, "matrix_encoding"),
+ MAPPER(FilterType, "filter_type"),
+ MAPPER(KaiserBeta, "kaiser_beta"),
+ MAPPER(DitherMethod, "dither_method")
+};
+#undef MAPPER
+
+}
+
FFMS_AudioSource::FFMS_AudioSource(const char *SourceFile, FFMS_Index &Index, int Track)
: Delay(0)
, MaxCacheBlocks(50)
, BytesPerSample(0)
+, NeedsResample(false)
, CurrentSample(-1)
, PacketNumber(0)
, CurrentFrame(NULL)
@@ -55,46 +85,14 @@ FFMS_AudioSource::FFMS_AudioSource(const char *SourceFile, FFMS_Index &Index, in
Index.AddRef();
}
-
#define EXCESSIVE_CACHE_SIZE 400
void FFMS_AudioSource::Init(const FFMS_Index &Index, int DelayMode) {
- // The first packet after a seek is often decoded incorrectly, which
- // makes it impossible to ever correctly seek back to the beginning, so
- // store the first block now
-
- // In addition, anything with the same PTS as the first packet can't be
- // distinguished from the first packet and so can't be seeked to, so
- // store those as well
-
- // Some of LAVF's splitters don't like to seek to the beginning of the
- // file (ts and?), so cache a few blocks even if PTSes are unique
- // Packet 7 is the last packet I've had be unseekable to, so cache up to
- // 10 for a bit of an extra buffer
- CacheIterator end = Cache.end();
- while (PacketNumber < Frames.size() &&
- ((Frames[0].PTS != ffms_av_nopts_value && Frames[PacketNumber].PTS == Frames[0].PTS) ||
- Cache.size() < 10)) {
-
- // Vorbis in particular seems to like having 60+ packets at the start
- // of the file with a PTS of 0, so we might need to expand the search
- // range to account for that.
- // Expanding slightly before it's strictly needed to ensure there's a
- // bit of space for an actual cache
- if (Cache.size() >= MaxCacheBlocks - 5) {
- if (MaxCacheBlocks >= EXCESSIVE_CACHE_SIZE)
- throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_ALLOCATION_FAILED,
- "Exceeded the search range for an initial valid audio PTS");
- MaxCacheBlocks *= 2;
- }
-
- DecodeNextBlock(&end);
- }
- // Store the iterator to the last element of the cache which is used for
- // correctness rather than speed, so that when looking for one to delete
- // we know how much to skip
- CacheNoDelete = Cache.end();
- --CacheNoDelete;
+ // Decode the first packet to ensure all properties are initialized
+ // Don't cache it since it might be in the wrong format
+ // Instead, leave it in DecodeFrame and it'll get cached later
+ while (DecodeFrame->nb_samples == 0)
+ DecodeNextBlock();
// Read properties of the audio which may not be available until the first
// frame has been decoded
@@ -104,6 +102,11 @@ void FFMS_AudioSource::Init(const FFMS_Index &Index, int DelayMode) {
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
"Codec returned zero size audio");
+ if (av_sample_fmt_is_planar(CodecContext->sample_fmt)) {
+ std::auto_ptr opt(CreateResampleOptions());
+ SetOutputFormat(opt.get());
+ }
+
if (DelayMode < FFMS_DELAY_NO_SHIFT)
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
"Bad audio delay compensation mode");
@@ -146,10 +149,118 @@ void FFMS_AudioSource::Init(const FFMS_Index &Index, int DelayMode) {
AP.NumSamples += Delay;
}
-void FFMS_AudioSource::InsertInterleaved(CacheIterator pos) {
- AudioBlock& block = *Cache.insert(pos, AudioBlock(CurrentSample, DecodeFrame->nb_samples));
+void FFMS_AudioSource::CacheBeginning() {
+ // Nothing to do if the cache is already populated
+ if (!Cache.empty()) return;
+
+ // The first frame is already decoded, so add it to the cache
+ CacheBlock(Cache.end());
+
+ // The first packet after a seek is often decoded incorrectly, which
+ // makes it impossible to ever correctly seek back to the beginning, so
+ // store the first block now
+
+ // In addition, anything with the same PTS as the first packet can't be
+ // distinguished from the first packet and so can't be seeked to, so
+ // store those as well
+
+ // Some of LAVF's splitters don't like to seek to the beginning of the
+ // file (ts and?), so cache a few blocks even if PTSes are unique
+ // Packet 7 is the last packet I've had be unseekable to, so cache up to
+ // 10 for a bit of an extra buffer
+ CacheIterator end = Cache.end();
+ while (PacketNumber < Frames.size() &&
+ ((Frames[0].PTS != ffms_av_nopts_value && Frames[PacketNumber].PTS == Frames[0].PTS) ||
+ Cache.size() < 10)) {
+
+ // Vorbis in particular seems to like having 60+ packets at the start
+ // of the file with a PTS of 0, so we might need to expand the search
+ // range to account for that.
+ // Expanding slightly before it's strictly needed to ensure there's a
+ // bit of space for an actual cache
+ if (Cache.size() >= MaxCacheBlocks - 5) {
+ if (MaxCacheBlocks >= EXCESSIVE_CACHE_SIZE)
+ throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_ALLOCATION_FAILED,
+ "Exceeded the search range for an initial valid audio PTS");
+ MaxCacheBlocks *= 2;
+ }
+ DecodeNextBlock(&end);
+ }
+ // Store the iterator to the last element of the cache which is used for
+ // correctness rather than speed, so that when looking for one to delete
+ // we know how much to skip
+ CacheNoDelete = Cache.end();
+ --CacheNoDelete;
+}
+
+void FFMS_AudioSource::SetOutputFormat(const FFMS_ResampleOptions *opt) {
+ if (!Cache.empty())
+ throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_USER,
+ "Cannot change the output format after audio decoding has begun");
+
+ BytesPerSample = av_get_bytes_per_sample(static_cast(opt->SampleFormat)) * av_get_channel_layout_nb_channels(opt->ChannelLayout);
+
+ NeedsResample =
+ opt->SampleFormat != (int)CodecContext->sample_fmt ||
+ opt->SampleRate != AP.SampleRate ||
+ opt->ChannelLayout != AP.ChannelLayout ||
+ opt->ForceResample;
+ if (!NeedsResample) return;
+
+ if (opt->SampleRate != AP.SampleRate)
+ throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNSUPPORTED,
+ "Sample rate changes are currently unsupported.");
+
+#ifdef WITH_AVRESAMPLE
+ if (opt->SampleRate != AP.SampleRate)
+ throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNSUPPORTED,
+ "Changing the audio sample rate is currently not supported");
+
+ std::auto_ptr oldOptions(ReadOptions(ResampleContext, resample_options));
+ SetOptions(opt, ResampleContext, resample_options);
+ av_opt_set_int(ResampleContext, "in_sample_rate", AP.SampleRate, 0);
+ av_opt_set_int(ResampleContext, "in_sample_fmt", CodecContext->sample_fmt, 0);
+ av_opt_set_int(ResampleContext, "in_channel_layout", AP.ChannelLayout, 0);
+
+ if (avresample_open(ResampleContext)) {
+ SetOptions(oldOptions.get(), ResampleContext, resample_options);
+ avresample_open(ResampleContext);
+ throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNKNOWN,
+ "Could not open avresample context");
+ }
+#else
+ if (opt->SampleFormat != AP.SampleFormat || opt->SampleRate != AP.SampleRate || opt->ChannelLayout != AP.ChannelLayout)
+ throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNSUPPORTED,
+ "FFMS was not built with resampling enabled. The only supported conversion is interleaving planar audio.");
+#endif
+}
+
+FFMS_ResampleOptions *FFMS_AudioSource::CreateResampleOptions() const {
+#ifdef WITH_AVRESAMPLE
+ FFMS_ResampleOptions *ret = ReadOptions(ResampleContext, resample_options);
+#else
+ FFMS_ResampleOptions *ret = new FFMS_ResampleOptions;
+ memset(ret, 0, sizeof(FFMS_ResampleOptions));
+#endif
+ ret->SampleRate = AP.SampleRate;
+ ret->SampleFormat = static_cast(AP.SampleFormat);
+ ret->ChannelLayout = AP.ChannelLayout;
+ return ret;
+}
+
+void FFMS_AudioSource::ResampleAndCache(CacheIterator pos) {
+ AudioBlock& block = *Cache.insert(pos, AudioBlock(CurrentSample, DecodeFrame->nb_samples));
block.Data.reserve(DecodeFrame->nb_samples * BytesPerSample);
+
+#ifdef WITH_AVRESAMPLE
+ block.Data.resize(block.Data.capacity());
+
+ uint8_t *OutPlanes[1] = { static_cast(&block.Data[0]) };
+ avresample_convert(ResampleContext,
+ OutPlanes, block.Data.size(), DecodeFrame->nb_samples,
+ DecodeFrame->extended_data, DecodeFrame->nb_samples * av_get_bytes_per_sample(CodecContext->sample_fmt), DecodeFrame->nb_samples);
+#else
int width = av_get_bytes_per_sample(CodecContext->sample_fmt);
uint8_t **Data = DecodeFrame->extended_data;
@@ -157,11 +268,12 @@ void FFMS_AudioSource::InsertInterleaved(CacheIterator pos) {
for (int c = 0; c < CodecContext->channels; ++c)
block.Data.insert(block.Data.end(), &Data[c][s * width], &Data[c][(s + 1) * width]);
}
+#endif
}
void FFMS_AudioSource::CacheBlock(CacheIterator pos) {
- if (CodecContext->channels > 1 && av_sample_fmt_is_planar(CodecContext->sample_fmt))
- InsertInterleaved(pos);
+ if (NeedsResample)
+ ResampleAndCache(pos);
else
Cache.insert(pos, AudioBlock(CurrentSample, DecodeFrame->nb_samples, DecodeFrame->extended_data[0], DecodeFrame->nb_samples * BytesPerSample));
@@ -179,9 +291,6 @@ void FFMS_AudioSource::CacheBlock(CacheIterator pos) {
}
void FFMS_AudioSource::DecodeNextBlock(CacheIterator *pos) {
- if (BytesPerSample == 0)
- BytesPerSample = av_get_bytes_per_sample(CodecContext->sample_fmt) * CodecContext->channels;
-
CurrentFrame = &Frames[PacketNumber];
AVPacket Packet;
@@ -235,6 +344,8 @@ void FFMS_AudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count) {
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT,
"Out of bounds audio samples requested");
+ CacheBeginning();
+
uint8_t *Dst = static_cast(Buf);
// Apply audio delay (if any) and fill any samples before the start time with zero
diff --git a/src/core/audiosource.h b/src/core/audiosource.h
index 3814a44965..23a6a46fb2 100644
--- a/src/core/audiosource.h
+++ b/src/core/audiosource.h
@@ -46,7 +46,6 @@ extern "C" {
#endif
struct FFMS_AudioSource {
-private:
struct AudioBlock {
int64_t Age;
int64_t Start;
@@ -83,11 +82,17 @@ struct FFMS_AudioSource {
// bytes per sample * number of channels
size_t BytesPerSample;
+ bool NeedsResample;
+ FFResampleContext ResampleContext;
+
// Insert the current audio frame into the cache
void CacheBlock(CacheIterator pos);
// Interleave the current audio frame and insert it into the cache
- void InsertInterleaved(CacheIterator pos);
+ void ResampleAndCache(CacheIterator pos);
+
+ // Cache the unseekable beginning of the file once the output format is set
+ void CacheBeginning();
// Called after seeking
virtual void Seek() { };
@@ -125,6 +130,9 @@ struct FFMS_AudioSource {
FFMS_Track *GetTrack() { return &Frames; }
const FFMS_AudioProperties& GetAudioProperties() const { return AP; }
void GetAudio(void *Buf, int64_t Start, int64_t Count);
+
+ FFMS_ResampleOptions *CreateResampleOptions() const;
+ void SetOutputFormat(const FFMS_ResampleOptions *opt);
};
class FFLAVFAudio : public FFMS_AudioSource {
diff --git a/src/core/ffms.cpp b/src/core/ffms.cpp
index c703225c52..49c06bd10b 100644
--- a/src/core/ffms.cpp
+++ b/src/core/ffms.cpp
@@ -256,6 +256,24 @@ FFMS_API(void) FFMS_ResetInputFormatV(FFMS_VideoSource *V) {
V->ResetInputFormat();
}
+FFMS_API(FFMS_ResampleOptions *) FFMS_CreateResampleOptions(FFMS_AudioSource *A) {
+ return A->CreateResampleOptions();
+}
+
+FFMS_API(void) FFMS_DestroyResampleOptions(FFMS_ResampleOptions *options) {
+ delete options;
+}
+
+FFMS_API(int) FFMS_SetOutputFormatA(FFMS_AudioSource *A, const FFMS_ResampleOptions *options, FFMS_ErrorInfo *ErrorInfo) {
+ ClearErrorInfo(ErrorInfo);
+ try {
+ A->SetOutputFormat(options);
+ } catch (FFMS_Exception &e) {
+ return e.CopyOut(ErrorInfo);
+ }
+ return FFMS_ERROR_SUCCESS;
+}
+
FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index) {
assert(Index != NULL);
if (Index == NULL)
diff --git a/src/core/utils.h b/src/core/utils.h
index f421ef07d7..85833ee2b9 100644
--- a/src/core/utils.h
+++ b/src/core/utils.h
@@ -31,9 +31,13 @@
extern "C" {
#include "stdiostream.h"
#include
+#include
#include
#include
#include
+#ifdef WITH_AVRESAMPLE
+#include
+#endif
}
// must be included after ffmpeg headers
@@ -133,26 +137,34 @@ class FFCodecContext {
}
};
-class ScopedFrame {
- AVFrame *frame;
+template
+class unknown_size {
+ T *ptr;
- ScopedFrame(ScopedFrame const&);
- ScopedFrame& operator=(ScopedFrame const&);
+ unknown_size(unknown_size const&);
+ unknown_size& operator=(unknown_size const&);
public:
- operator AVFrame*() const { return frame; }
- AVFrame *operator->() const { return frame; }
+ operator T*() const { return ptr; }
+ operator void*() const { return ptr; }
+ T *operator->() const { return ptr; }
+
+ unknown_size() : ptr(Alloc()) { }
+ ~unknown_size() { Del(&ptr); }
+};
+class ScopedFrame : public unknown_size {
+public:
void reset() {
- if (frame)
- avcodec_get_frame_defaults(frame);
- else
- frame = avcodec_alloc_frame();
+ avcodec_get_frame_defaults(*this);
}
-
- ScopedFrame() : frame(0) { }
- ~ScopedFrame() { if (frame) avcodec_free_frame(&frame); }
};
+#ifdef WITH_AVRESAMPLE
+typedef unknown_size FFResampleContext;
+#else
+typedef struct {} FFResampleContext;
+#endif
+
inline void DeleteHaaliCodecContext(AVCodecContext *CodecContext) {
av_freep(&CodecContext->extradata);
av_freep(&CodecContext);
@@ -228,4 +240,68 @@ void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext);
void FlushBuffers(AVCodecContext *CodecContext);
+namespace optdetail {
+ template
+ T get_av_opt(void *v, const char *name) {
+ return static_cast(av_get_int(v, name, 0));
+ }
+
+ template<>
+ inline double get_av_opt(void *v, const char *name) {
+ return av_get_double(v, name, 0);
+ }
+
+ template
+ void set_av_opt(void *v, const char *name, T value) {
+ av_opt_set_int(v, name, value, 0);
+ }
+
+ template<>
+ inline void set_av_opt(void *v, const char *name, double value) {
+ av_opt_set_double(v, name, value, 0);
+ }
+}
+
+template
+class OptionMapper {
+ struct OptionMapperBase {
+ virtual void ToOpt(const FFMS_Struct *src, void *dst) const=0;
+ virtual void FromOpt(FFMS_Struct *dst, void *src) const=0;
+ };
+
+ template
+ class OptionMapperImpl : public OptionMapperBase {
+ T (FFMS_Struct::*ptr);
+ const char *name;
+
+ public:
+ OptionMapperImpl(T (FFMS_Struct::*ptr), const char *name) : ptr(ptr), name(name) { }
+ void ToOpt(const FFMS_Struct *src, void *dst) const { optdetail::set_av_opt(dst, name, src->*ptr); }
+ void FromOpt(FFMS_Struct *dst, void *src) const { dst->*ptr = optdetail::get_av_opt(src, name); }
+ };
+
+ OptionMapperBase *impl;
+
+public:
+ template
+ OptionMapper(const char *opt_name, T (FFMS_Struct::*member)) : impl(new OptionMapperImpl(member, opt_name)) { }
+
+ void ToOpt(const FFMS_Struct *src, void *dst) const { impl->ToOpt(src, dst); }
+ void FromOpt(FFMS_Struct *dst, void *src) const { impl->FromOpt(dst, src); }
+};
+
+template
+T *ReadOptions(void *opt, OptionMapper (&options)[N]) {
+ T *ret = new T;
+ for (int i = 0; i < N; ++i)
+ options[i].FromOpt(ret, opt);
+ return ret;
+}
+
+template
+void SetOptions(const T* src, void *opt, OptionMapper (&options)[N]) {
+ for (int i = 0; i < N; ++i)
+ options[i].ToOpt(src, opt);
+}
+
#endif