[PATCH] xaudio2: Use ffmpeg to convert non-PCM formats

Andrew Eikum aeikum at codeweavers.com
Wed Sep 9 10:26:29 CDT 2015


---

This is the controversial patch to introduce a dependency on FFmpeg. In
this patch, we use it as a build-time dependency, and have no audio
conversion capabilities if it is not present at build-time. xaudio2
built with FFmpeg will fail to load if FFmpeg is not available at
run-time.

I'm not aware of *any* games that do not require some audio conversion
to play back audio. I'm only aware of one game (Doom 3) that doesn't use
WMA. A WMA decoder is virtually required to have audio in games that use
xaudio2, so there's little point in loading xaudio2 without a WMA
decoder. Failing to load xaudio2 if FFmpeg is not present is no great
loss.


Some people think the FFmpeg API changes too often, and will create a
maintenance burden or difficulties bisecting Wine when the FFmpeg API
changes. We use the following FFmpeg exports:

Available since 2013:
av_frame_unref, av_frame_free, av_frame_alloc

Available since 2012:
avcodec_find_decoder

Available since 2011:
avcodec_decode_audio4, av_sample_fmt_is_planar, avcodec_alloc_context3,
avcodec_open2, av_get_bytes_per_sample

Available since 2002:
av_free, avcodec_register_all, avcodec_close


I've discussed other options with people on IRC, including:

Build and distribute our own Windows FFmpeg package, or ask the user to
install a Windows FFmpeg package in their wineprefix, instead of relying
on the system FFmpeg. Maybe a workable solution, but we'd have to build
and distribute yet another package, or rely on a 3rd party to do so.
There may also be optimization concerns with a package built for Windows
instead of natively on Linux/OSX.

Find a different WMA decoding library. While one exists (libwmapro), it
is simply pulled out of FFmpeg and converted for use on iPods. It's
several years of out date with FFmpeg's WMA decoder. No other open
source WMA decoder exists that I'm aware of.

Pull FFmpeg's WMA code out and use it in Wine directly. This would
require a significant amount of effort and ongoing work to pull in
updates. I'm not convinced this is worth the effort.


There didn't seem to be any consensus. I'm not convinced any of those
options are actually better than just using the system FFmpeg
installation. So, I'm submitting this patch to at least have something
concrete to start the discussion around.

If you are opposed to this technique, please explain what approach you
would prefer instead.

For some more reading, see:

https://www.winehq.org/pipermail/wine-devel/2015-August/108820.html

https://www.winehq.org/pipermail/wine-users/2015-August/107301.html

Andrew

 configure.ac                |  33 ++++
 dlls/xaudio2_7/Makefile.in  |   2 +-
 dlls/xaudio2_7/xaudio_dll.c | 378 +++++++++++++++++++++++++++++++++++++++++++-
 include/config.h.in         |   6 +
 include/mmreg.h             |   5 +
 5 files changed, 415 insertions(+), 9 deletions(-)

diff --git a/configure.ac b/configure.ac
index aec53f6..a7f328a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -43,6 +43,7 @@ AC_ARG_WITH(cups,      AS_HELP_STRING([--without-cups],[do not use CUPS]))
 AC_ARG_WITH(curses,    AS_HELP_STRING([--without-curses],[do not use (n)curses]),
             [if test "x$withval" = "xno"; then ac_cv_header_ncurses_h=no; ac_cv_header_curses_h=no; fi])
 AC_ARG_WITH(dbus,      AS_HELP_STRING([--without-dbus],[do not use DBus (dynamic device support)]))
+AC_ARG_WITH(ffmpeg,    AS_HELP_STRING([--without-ffmpeg],[do not use the FFmpeg library]))
 AC_ARG_WITH(fontconfig,AS_HELP_STRING([--without-fontconfig],[do not use fontconfig]))
 AC_ARG_WITH(freetype,  AS_HELP_STRING([--without-freetype],[do not use the FreeType library]))
 AC_ARG_WITH(gettext,   AS_HELP_STRING([--without-gettext],[do not use gettext]))
@@ -1730,6 +1731,38 @@ WINE_NOTICE_WITH(mpg123,[test "x$ac_cv_lib_mpg123_mpg123_feed" != xyes -a x"$ac_
                  [libmpg123 ${notice_platform}development files not found (or too old), mp3 codec won't be supported.])
 test "x$ac_cv_lib_mpg123_mpg123_feed" = xyes -o "x$ac_cv_header_AudioToolbox_AudioConverter_h" = xyes || enable_winemp3_acm=${enable_winemp3_acm:-no}
 
+dnl **** Check for FFmpeg's libavutil ****
+if test "x$with_ffmpeg" != "xno"
+then
+    WINE_PACKAGE_FLAGS(LIBAVUTIL,[libavutil],[-llibavutil],,,
+        [AC_CHECK_HEADERS([libavutil/avutil.h])
+        if test "$ac_cv_header_libavutil_avutil_h" = "yes"
+        then
+            AC_CHECK_LIB(avutil,av_frame_alloc,[:],[LIBAVUTIL_LIBS=""],[$LIBAVUTIL_LIBS])
+        else
+            libavutil_CFLAGS=""
+            libavutil_LIBS=""
+        fi])
+fi
+WINE_NOTICE_WITH(libavutil,[test "x$ac_cv_lib_avutil_av_frame_alloc" != xyes],
+                 [libavutil ${notice_platform}development files not found (or too old), XAudio2 WMA conversion won't be supported.])
+
+dnl **** Check for FFmpeg's libavcodec ****
+if test "x$with_ffmpeg" != "xno"
+then
+    WINE_PACKAGE_FLAGS(LIBAVCODEC,[libavcodec],[-llibavcodec],,,
+        [AC_CHECK_HEADERS([libavcodec/avcodec.h])
+        if test "$ac_cv_header_libavcodec_avcodec_h" = "yes"
+        then
+            AC_CHECK_LIB(avcodec,avcodec_find_decoder,[:],[LIBAVCODEC_LIBS=""],[$LIBAVCODEC_LIBS])
+        else
+            libavcodec_CFLAGS=""
+            libavcodec_LIBS=""
+        fi])
+fi
+WINE_NOTICE_WITH(libavcodec,[test "x$ac_cv_lib_avcodec_avcodec_find_decoder" != xyes],
+                 [libavcodec ${notice_platform}development files not found (or too old), XAudio2 WMA conversion won't be supported.])
+
 dnl **** Check for OpenAL 1.1 ****
 if test "$ac_cv_header_AL_al_h" = "yes"
 then
diff --git a/dlls/xaudio2_7/Makefile.in b/dlls/xaudio2_7/Makefile.in
index 5a09c82..9645ac0 100644
--- a/dlls/xaudio2_7/Makefile.in
+++ b/dlls/xaudio2_7/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = xaudio2_7.dll
 IMPORTS   = advapi32 kernel32 ole32 user32 uuid
-EXTRALIBS = $(OPENAL_LIBS)
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
 
 C_SRCS = \
 	xaudio_dll.c
diff --git a/dlls/xaudio2_7/xaudio_dll.c b/dlls/xaudio2_7/xaudio_dll.c
index 739c919..072d018 100644
--- a/dlls/xaudio2_7/xaudio_dll.c
+++ b/dlls/xaudio2_7/xaudio_dll.c
@@ -17,6 +17,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "config.h"
+
 #include <stdarg.h>
 
 #define NONAMELESSUNION
@@ -42,6 +44,11 @@
 #include "mmdeviceapi.h"
 #include "audioclient.h"
 
+#if HAVE_LIBAVCODEC_AVCODEC_H
+#define HAVE_FFMPEG 1
+#include <libavcodec/avcodec.h>
+#endif
+
 #include <AL/al.h>
 #include <AL/alc.h>
 #include <AL/alext.h>
@@ -53,6 +60,11 @@ static void (ALC_APIENTRY *palcRenderSamplesSOFT)(ALCdevice*, ALCvoid*, ALCsizei
 
 static HINSTANCE instance;
 
+#define IS_WMA(tag) (tag == WAVE_FORMAT_MSAUDIO1 || \
+        tag == WAVE_FORMAT_WMAUDIO2 || \
+        tag == WAVE_FORMAT_WMAUDIO3 || \
+        tag == WAVE_FORMAT_WMAUDIO_LOSSLESS)
+
 static void dump_fmt(const WAVEFORMATEX *fmt)
 {
     TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
@@ -61,6 +73,11 @@ static void dump_fmt(const WAVEFORMATEX *fmt)
     DOCASE(WAVE_FORMAT_PCM)
     DOCASE(WAVE_FORMAT_IEEE_FLOAT)
     DOCASE(WAVE_FORMAT_EXTENSIBLE)
+    DOCASE(WAVE_FORMAT_ADPCM)
+    DOCASE(WAVE_FORMAT_MSAUDIO1)
+    DOCASE(WAVE_FORMAT_WMAUDIO2)
+    DOCASE(WAVE_FORMAT_WMAUDIO3)
+    DOCASE(WAVE_FORMAT_WMAUDIO_LOSSLESS)
 #undef DOCASE
     default:
         TRACE("Unknown");
@@ -102,6 +119,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD reason, void *pReserved)
             return FALSE;
         }
 
+#if HAVE_FFMPEG
+        avcodec_register_all();
+#endif
         break;
     }
     return TRUE;
@@ -158,12 +178,20 @@ typedef struct _XA2SourceImpl {
     XA2Buffer buffers[XAUDIO2_MAX_QUEUED_BUFFERS];
     UINT32 first_buf, cur_buf, nbufs, in_al_bytes;
 
+    UINT32 scratch_bytes, convert_bytes;
+    BYTE *scratch_buf, *convert_buf;
+
     ALuint al_src;
     /* most cases will only need about 4 AL buffers, but some corner cases
      * could require up to MAX_QUEUED_BUFFERS */
     ALuint al_bufs[XAUDIO2_MAX_QUEUED_BUFFERS];
     DWORD first_al_buf, al_bufs_used;
 
+#if HAVE_FFMPEG
+    AVCodecContext *conv_ctx;
+    AVFrame *conv_frame;
+#endif
+
     struct list entry;
 } XA2SourceImpl;
 
@@ -489,6 +517,10 @@ static void WINAPI XA2SRC_DestroyVoice(IXAudio2SourceVoice *iface)
     }
 
     HeapFree(GetProcessHeap(), 0, This->fmt);
+    HeapFree(GetProcessHeap(), 0, This->scratch_buf);
+    This->scratch_buf = NULL;
+    HeapFree(GetProcessHeap(), 0, This->convert_buf);
+    This->convert_buf = NULL;
 
     alDeleteBuffers(XAUDIO2_MAX_QUEUED_BUFFERS, This->al_bufs);
     alDeleteSources(1, &This->al_src);
@@ -500,6 +532,17 @@ static void WINAPI XA2SRC_DestroyVoice(IXAudio2SourceVoice *iface)
     This->first_buf = 0;
     This->cur_buf = 0;
 
+#if HAVE_FFMPEG
+    if(This->conv_ctx){
+        HeapFree(GetProcessHeap(), 0, This->conv_ctx->extradata);
+        av_frame_free(&This->conv_frame);
+        This->conv_frame = NULL;
+        avcodec_close(This->conv_ctx);
+        av_free(This->conv_ctx);
+        This->conv_ctx = NULL;
+    }
+#endif
+
     LeaveCriticalSection(&This->lock);
 }
 
@@ -588,6 +631,97 @@ static ALenum get_al_format(const WAVEFORMATEX *fmt)
     return 0;
 }
 
+#if HAVE_FFMPEG
+static enum AVCodecID get_ffmpeg_format(const WAVEFORMATEX *pSourceFormat)
+{
+    switch(pSourceFormat->wFormatTag){
+    case WAVE_FORMAT_MSAUDIO1:
+        return AV_CODEC_ID_WMAV1;
+    case WAVE_FORMAT_WMAUDIO2:
+        return AV_CODEC_ID_WMAV2;
+    case WAVE_FORMAT_WMAUDIO3:
+        return AV_CODEC_ID_WMAPRO;
+    case WAVE_FORMAT_WMAUDIO_LOSSLESS:
+        return AV_CODEC_ID_WMALOSSLESS;
+    case WAVE_FORMAT_ADPCM:
+        return AV_CODEC_ID_ADPCM_MS;
+    }
+    return 0;
+}
+
+static ALenum ffmpeg_to_al_fmt(enum AVSampleFormat fmt, int channels)
+{
+    switch(fmt){
+    case AV_SAMPLE_FMT_U8:
+    case AV_SAMPLE_FMT_U8P:
+        switch(channels){
+        case 1:
+            return AL_FORMAT_MONO8;
+        case 2:
+            return AL_FORMAT_STEREO8;
+        case 4:
+            return AL_FORMAT_QUAD8;
+        case 6:
+            return AL_FORMAT_51CHN8;
+        case 7:
+            return AL_FORMAT_61CHN8;
+        case 8:
+            return AL_FORMAT_71CHN8;
+        }
+        break;
+    case AV_SAMPLE_FMT_S16:
+    case AV_SAMPLE_FMT_S16P:
+        switch(channels){
+        case 1:
+            return AL_FORMAT_MONO16;
+        case 2:
+            return AL_FORMAT_STEREO16;
+        case 4:
+            return AL_FORMAT_QUAD16;
+        case 6:
+            return AL_FORMAT_51CHN16;
+        case 7:
+            return AL_FORMAT_61CHN16;
+        case 8:
+            return AL_FORMAT_71CHN16;
+        }
+        break;
+    case AV_SAMPLE_FMT_S32:
+    case AV_SAMPLE_FMT_S32P:
+        switch(channels){
+        /* TODO: mono/stereo? */
+        case 4:
+            return AL_FORMAT_QUAD32;
+        case 6:
+            return AL_FORMAT_51CHN32;
+        case 7:
+            return AL_FORMAT_61CHN32;
+        case 8:
+            return AL_FORMAT_71CHN32;
+        }
+    case AV_SAMPLE_FMT_FLT:
+    case AV_SAMPLE_FMT_FLTP:
+        switch(channels){
+        case 1:
+            return AL_FORMAT_MONO_FLOAT32;
+        case 2:
+            return AL_FORMAT_STEREO_FLOAT32;
+        }
+    case AV_SAMPLE_FMT_DBL:
+    case AV_SAMPLE_FMT_DBLP:
+        switch(channels){
+        case 1:
+            return AL_FORMAT_MONO_DOUBLE_EXT;
+        case 2:
+            return AL_FORMAT_STEREO_DOUBLE_EXT;
+        }
+    default:
+        break;
+    }
+    return 0;
+}
+#endif
+
 static HRESULT WINAPI XA2SRC_SubmitSourceBuffer(IXAudio2SourceVoice *iface,
         const XAUDIO2_BUFFER *pBuffer, const XAUDIO2_BUFFER_WMA *pBufferWMA)
 {
@@ -1616,18 +1750,113 @@ static HRESULT WINAPI IXAudio2Impl_CreateSourceVoice(IXAudio2 *iface,
 
     src->al_fmt = get_al_format(pSourceFormat);
     if(!src->al_fmt){
-        src->in_use = FALSE;
-        WARN("OpenAL can't convert this format!\n");
-        return AUDCLNT_E_UNSUPPORTED_FORMAT;
-    }
+#if HAVE_FFMPEG
+        enum AVCodecID cid;
+        AVCodec *codec;
 
-    src->submit_blocksize = pSourceFormat->nBlockAlign;
+        TRACE("OpenAL can't use this format, so using FFmpeg\n");
+
+        cid = get_ffmpeg_format(pSourceFormat);
+        if(!cid){
+            WARN("Don't know how to convert this format to an FFmpeg codec\n");
+            src->in_use = FALSE;
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        codec = avcodec_find_decoder(cid);
+        if(!codec){
+            WARN("FFmpeg can't convert this format (0x%x), so failing\n", cid);
+            src->in_use = FALSE;
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        src->conv_ctx = avcodec_alloc_context3(codec);
+        if(!src->conv_ctx){
+            WARN("avcodec_alloc_context3 failed\n");
+            src->in_use = FALSE;
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        src->conv_ctx->bit_rate = pSourceFormat->nAvgBytesPerSec * 8;
+        src->conv_ctx->channels = pSourceFormat->nChannels;
+        src->conv_ctx->sample_rate = pSourceFormat->nSamplesPerSec;
+        src->conv_ctx->block_align = pSourceFormat->nBlockAlign;
+        src->conv_ctx->bits_per_coded_sample = pSourceFormat->wBitsPerSample;
+        src->conv_ctx->extradata_size = pSourceFormat->cbSize;
+        if(pSourceFormat->cbSize){
+            src->conv_ctx->extradata = HeapAlloc(GetProcessHeap(), 0, pSourceFormat->cbSize + FF_INPUT_BUFFER_PADDING_SIZE);
+            memcpy(src->conv_ctx->extradata, (&pSourceFormat->cbSize) + 1, pSourceFormat->cbSize);
+        }else if(IS_WMA(pSourceFormat->wFormatTag)){
+            /* xWMA doesn't provide the extradata info that FFmpeg needs to
+             * decode WMA data, so we create some fake extradata. This is taken
+             * from <ffmpeg/libavformat/xwma.c>. */
+            TRACE("synthesizing extradata for xWMA\n");
+            src->conv_ctx->extradata_size = 6;
+            src->conv_ctx->extradata = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pSourceFormat->cbSize + FF_INPUT_BUFFER_PADDING_SIZE);
+            src->conv_ctx->extradata[4] = 31;
+        }
+
+        if(avcodec_open2(src->conv_ctx, codec, NULL) < 0){
+            WARN("avcodec_open2 failed\n");
+            HeapFree(GetProcessHeap(), 0, src->conv_ctx->extradata);
+            av_free(src->conv_ctx);
+            src->conv_ctx = NULL;
+            src->in_use = FALSE;
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        src->conv_frame = av_frame_alloc();
+        if(!src->conv_ctx){
+            WARN("av_frame_alloc failed\n");
+            avcodec_close(src->conv_ctx);
+            HeapFree(GetProcessHeap(), 0, src->conv_ctx->extradata);
+            av_free(src->conv_ctx);
+            src->conv_ctx = NULL;
+            src->in_use = FALSE;
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        src->al_fmt = ffmpeg_to_al_fmt(src->conv_ctx->sample_fmt, pSourceFormat->nChannels);
+        if(!src->al_fmt){
+            WARN("OpenAL can't use FFmpeg output format\n");
+            av_frame_free(&src->conv_frame);
+            src->conv_frame = NULL;
+            avcodec_close(src->conv_ctx);
+            HeapFree(GetProcessHeap(), 0, src->conv_ctx->extradata);
+            av_free(src->conv_ctx);
+            src->conv_ctx = NULL;
+            src->in_use = FALSE;
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        src->submit_blocksize = av_get_bytes_per_sample(src->conv_ctx->sample_fmt);
+
+        src->scratch_bytes = This->period_frames * 1.5 * src->submit_blocksize;
+        src->scratch_buf = HeapAlloc(GetProcessHeap(), 0, src->scratch_bytes);
+
+        src->convert_bytes = pSourceFormat->nBlockAlign + FF_INPUT_BUFFER_PADDING_SIZE;
+        src->convert_buf = HeapAlloc(GetProcessHeap(), 0, src->convert_bytes);
+#else
+        WARN("OpenAL can't use this format and no FFmpeg, so giving up\n");
+#endif
+    }else
+        src->submit_blocksize = pSourceFormat->nBlockAlign;
 
     src->fmt = copy_waveformat(pSourceFormat);
 
     hr = XA2SRC_SetOutputVoices(&src->IXAudio2SourceVoice_iface, pSendList);
     if(FAILED(hr)){
         src->in_use = FALSE;
+#if HAVE_FFMPEG
+        if(src->conv_ctx){
+            av_frame_free(&src->conv_frame);
+            src->conv_frame = NULL;
+            avcodec_close(src->conv_ctx);
+            HeapFree(GetProcessHeap(), 0, src->conv_ctx->extradata);
+            av_free(src->conv_ctx);
+            src->conv_ctx = NULL;
+        }
+#endif
         return hr;
     }
 
@@ -2911,9 +3140,142 @@ static BOOL xa2buffer_queue_period(XA2SourceImpl *src, XA2Buffer *buf, ALuint al
         return FALSE;
     }
 
-    submit_bytes = min(src->xa2->period_frames * src->submit_blocksize, buf->xa2buffer.AudioBytes - buf->offs_bytes);
-    submit_buf = buf->xa2buffer.pAudioData + buf->offs_bytes;
-    buf->offs_bytes += submit_bytes;
+#if HAVE_FFMPEG
+    if(src->conv_ctx){
+        DWORD scratch_offs_bytes = 0;
+        AVPacket avpkt = {0};
+        BOOL using_convert = FALSE;
+
+        avpkt.size = src->fmt->nBlockAlign;
+        avpkt.data = (unsigned char*)buf->xa2buffer.pAudioData + buf->offs_bytes;
+
+        /* convert at least a period into scratch_buf */
+        while(scratch_offs_bytes < src->xa2->period_frames * src->submit_blocksize){
+            int used_bytes, got_frame;
+
+            avpkt.pts = avpkt.dts = AV_NOPTS_VALUE;
+
+            if(buf->offs_bytes == buf->xa2buffer.AudioBytes){
+                avpkt.size = 0;
+                avpkt.data = NULL;
+            }else if(buf->offs_bytes > buf->xa2buffer.AudioBytes){
+                ERR("Shouldn't happen: Bad offset: %u > %u\n", buf->offs_bytes, buf->xa2buffer.AudioBytes);
+                return FALSE;
+            }else if(!using_convert &&
+                    buf->offs_bytes + avpkt.size + FF_INPUT_BUFFER_PADDING_SIZE > buf->xa2buffer.AudioBytes){
+                UINT32 remain = buf->xa2buffer.AudioBytes - buf->offs_bytes;
+                /* Unfortunately, the FFmpeg API requires that a number of
+                 * extra bytes must be available past the end of the buffer.
+                 * The xaudio2 client probably hasn't done this, so we have to
+                 * perform a copy near the end of the buffer. */
+                TRACE("hitting end of buffer. copying %u + %u bytes into %u buffer\n",
+                        remain, FF_INPUT_BUFFER_PADDING_SIZE, src->convert_bytes);
+                if(src->convert_bytes < remain + FF_INPUT_BUFFER_PADDING_SIZE){
+                    src->convert_bytes = remain + FF_INPUT_BUFFER_PADDING_SIZE;
+                    TRACE("buffer too small, expanding to %u\n", src->convert_bytes);
+                    src->convert_buf = HeapReAlloc(GetProcessHeap(), 0, src->convert_buf, src->convert_bytes);
+                }
+                memcpy(src->convert_buf, buf->xa2buffer.pAudioData + buf->offs_bytes, remain);
+                memset(src->convert_buf + remain, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+                avpkt.data = src->convert_buf;
+                using_convert = TRUE;
+            }
+
+            used_bytes = avcodec_decode_audio4(src->conv_ctx,
+                    src->conv_frame, &got_frame, &avpkt);
+
+            if(used_bytes < 0){
+                TRACE("an error occured\n");
+                return FALSE;
+            }
+
+            if(avpkt.size){
+                avpkt.data += used_bytes;
+                buf->offs_bytes += used_bytes;
+            }
+
+            if(got_frame){
+                DWORD to_copy_bytes = src->conv_frame->nb_samples * src->conv_ctx->channels * src->submit_blocksize;
+
+                if(!to_copy_bytes){
+                    if(!avpkt.size)
+                        break;
+                    continue;
+                }
+
+                while(scratch_offs_bytes + to_copy_bytes >= src->scratch_bytes){
+                    src->scratch_bytes *= 2;
+                    src->scratch_buf = HeapReAlloc(GetProcessHeap(), 0, src->scratch_buf, src->scratch_bytes);
+                }
+
+                if(av_sample_fmt_is_planar(src->conv_ctx->sample_fmt)){
+                    int s, c;
+                    uint8_t **source, *dst;
+                    uint16_t *dst16;
+                    uint32_t *dst32;
+                    uint64_t *dst64;
+
+                    /* one buffer per channel, but openal needs interleaved, so
+                     * interleave samples into scratch buf */
+                    dst = src->scratch_buf + scratch_offs_bytes;
+                    source = src->conv_frame->data;
+
+                    switch(src->submit_blocksize){
+                    case 1:
+                        for(s = 0; s < src->conv_frame->nb_samples; ++s)
+                            for(c = 0; c < src->conv_ctx->channels; ++c)
+                                *(dst++) = source[c][s];
+                        break;
+                    case 2:
+                        dst16 = (uint16_t*)dst;
+                        for(s = 0; s < src->conv_frame->nb_samples; ++s)
+                            for(c = 0; c < src->conv_ctx->channels; ++c)
+                                *(dst16++) = ((uint16_t*)(source[c]))[s];
+                        break;
+                    case 4:
+                        dst32 = (uint32_t*)dst;
+                        for(s = 0; s < src->conv_frame->nb_samples; ++s)
+                            for(c = 0; c < src->conv_ctx->channels; ++c)
+                                *(dst32++) = ((uint32_t*)(source[c]))[s];
+                        break;
+                    case 8:
+                        dst64 = (uint64_t*)dst;
+                        for(s = 0; s < src->conv_frame->nb_samples; ++s)
+                            for(c = 0; c < src->conv_ctx->channels; ++c)
+                                *(dst64++) = ((uint64_t*)(source[c]))[s];
+                        break;
+                    default:
+                        for(s = 0; s < src->conv_frame->nb_samples; ++s)
+                            for(c = 0; c < src->conv_ctx->channels; ++c){
+                                memcpy(dst, &source[c][src->submit_blocksize * s], src->submit_blocksize);
+                                dst += src->submit_blocksize;
+                            }
+                        break;
+                    }
+
+                    scratch_offs_bytes += to_copy_bytes;
+                }else{
+                    /* copy into scratch buf */
+                    memcpy(src->scratch_buf + scratch_offs_bytes, src->conv_frame->data[0], to_copy_bytes);
+                    scratch_offs_bytes += to_copy_bytes;
+                }
+
+                if(src->conv_ctx->refcounted_frames)
+                    av_frame_unref(src->conv_frame);
+            }else if(!avpkt.size)
+                break;
+        }
+
+        submit_bytes = scratch_offs_bytes;
+        submit_buf = src->scratch_buf;
+    }else{
+#endif
+        submit_bytes = min(src->xa2->period_frames * src->submit_blocksize, buf->xa2buffer.AudioBytes - buf->offs_bytes);
+        submit_buf = buf->xa2buffer.pAudioData + buf->offs_bytes;
+        buf->offs_bytes += submit_bytes;
+#if HAVE_FFMPEG
+    }
+#endif
 
     alBufferData(al_buf, src->al_fmt, submit_buf, submit_bytes,
             src->fmt->nSamplesPerSec);
diff --git a/include/config.h.in b/include/config.h.in
index eb61a94..772436a 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -339,6 +339,12 @@
 /* Define to 1 if you have the `ldap_parse_vlv_control' function. */
 #undef HAVE_LDAP_PARSE_VLV_CONTROL
 
+/* Define to 1 if you have the <libavcodec/avcodec.h> header file. */
+#undef HAVE_LIBAVCODEC_AVCODEC_H
+
+/* Define to 1 if you have the <libavutil/avutil.h> header file. */
+#undef HAVE_LIBAVUTIL_AVUTIL_H
+
 /* Define to 1 if you have the `gettextpo' library (-lgettextpo). */
 #undef HAVE_LIBGETTEXTPO
 
diff --git a/include/mmreg.h b/include/mmreg.h
index e0b1dd0..ec44b52 100644
--- a/include/mmreg.h
+++ b/include/mmreg.h
@@ -108,6 +108,11 @@ typedef struct _WAVEFORMATEX {
 #define  WAVE_FORMAT_MPEG			0x0050	/*  Microsoft Corporation  */
 #define  WAVE_FORMAT_MPEGLAYER3			0x0055
 #define  WAVE_FORMAT_MSRT24			0x0082  /*  Microsoft Corporation */
+#define  WAVE_FORMAT_MSAUDIO1			0x0160
+#define  WAVE_FORMAT_WMAUDIO2			0x0161
+#define  WAVE_FORMAT_WMAUDIO3			0x0162
+#define  WAVE_FORMAT_WMAUDIO_LOSSLESS		0x0163
+
 #define  WAVE_FORMAT_CREATIVE_ADPCM		0x0200	/*  Creative Labs, Inc  */
 #define  WAVE_FORMAT_CREATIVE_FASTSPEECH8	0x0202	/*  Creative Labs, Inc  */
 #define  WAVE_FORMAT_CREATIVE_FASTSPEECH10	0x0203	/*  Creative Labs, Inc  */
-- 
2.5.1




More information about the wine-patches mailing list