diff options
4 files changed, 378 insertions, 108 deletions
diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java index b0181bd7d..2f92f9bf3 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java @@ -59,12 +59,13 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { private static final List<String> glueLibNames = new ArrayList<String>(); // none - private static final int symbolCount = 43; + private static final int symbolCount = 51; private static final String[] symbolNames = { "avcodec_version", "avformat_version", -/* 3 */ "avutil_version", - + "avutil_version", +/* 4 */ "avresample_version", + // libavcodec "avcodec_close", "avcodec_string", @@ -83,15 +84,17 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "av_free_packet", "avcodec_decode_audio4", // 53.25.0 (opt) "avcodec_decode_audio3", // 52.23.0 -/* 21 */ "avcodec_decode_video2", // 52.23.0 +/* 22 */ "avcodec_decode_video2", // 52.23.0 // libavutil "av_pix_fmt_descriptors", "av_frame_unref", // 55.0.0 (opt) "av_free", "av_get_bits_per_pixel", -/* 26 */ "av_samples_get_buffer_size", - + "av_samples_get_buffer_size", + "av_get_bytes_per_sample", // 51.4.0 +/* 29 */ "av_opt_set_int", // 51.12.0 + // libavformat "avformat_alloc_context", "avformat_free_context", // 52.96.0 (opt) @@ -109,7 +112,14 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "avformat_network_init", // 53.13.0 (opt) "avformat_network_deinit", // 53.13.0 (opt) "avformat_find_stream_info", // 53.3.0 (opt) -/* 43 */ "av_find_stream_info", +/* 46 */ "av_find_stream_info", + + // libavresample + "avresample_alloc_context", // 1.0.1 + "avresample_open", + "avresample_close", + "avresample_free", +/* 51 */ "avresample_convert" }; // alternate symbol names @@ -128,23 +138,34 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "avformat_seek_file", // ??? (opt) "avcodec_free_frame", // 54.28.0 (opt) "av_frame_unref", // 55.0.0 (opt) + + // libavresample + "avresample_version", // 1.0.1 + "avresample_alloc_context", // 1.0.1 + "avresample_open", + "avresample_close", + "avresample_free", + "avresample_convert", }; private static long[] symbolAddr; private static final boolean ready; private static final boolean libsLoaded; + private static final boolean avresampleLoaded; // optional static { // native ffmpeg media player implementation is included in jogl_desktop and jogl_mobile GLProfile.initSingleton(); boolean _ready = false; boolean[] _libsLoaded= { false }; + boolean[] _avresampleLoaded= { false }; try { - _ready = initSymbols(_libsLoaded); + _ready = initSymbols(_libsLoaded, _avresampleLoaded); } catch (Throwable t) { t.printStackTrace(); } libsLoaded = _libsLoaded[0]; + avresampleLoaded = _avresampleLoaded[0]; ready = _ready; if(!libsLoaded) { System.err.println("LIB_AV Not Available"); @@ -154,9 +175,10 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { } static boolean libsLoaded() { return libsLoaded; } + static boolean avResampleLoaded() { return avresampleLoaded; } static boolean initSingleton() { return ready; } - private static final boolean initSymbols(boolean[] libsLoaded) { + private static final boolean initSymbols(boolean[] libsLoaded, boolean[] avresampleLoaded) { libsLoaded[0] = false; final DynamicLibraryBundle dl = AccessController.doPrivileged(new PrivilegedAction<DynamicLibraryBundle>() { public DynamicLibraryBundle run() { @@ -167,10 +189,12 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { final boolean avcodecLoaded = dl.isToolLibLoaded(2); if(!avutilLoaded || !avformatLoaded || !avcodecLoaded) { throw new RuntimeException("FFMPEG Tool library incomplete: [ avutil "+avutilLoaded+", avformat "+avformatLoaded+", avcodec "+avcodecLoaded+"]"); - } + } + avresampleLoaded[0] = dl.isToolLibLoaded(3); + /** Ignore .. due to optional libavresample if(!dl.isToolLibComplete()) { throw new RuntimeException("FFMPEG Tool libraries incomplete"); - } + } */ libsLoaded[0] = true; if(symbolNames.length != symbolCount) { throw new InternalError("XXX0 "+symbolNames.length+" != "+symbolCount); @@ -205,6 +229,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { } } ); // validate results + boolean res = true; for(int i = 0; i<symbolCount; i++) { if( 0 == symbolAddr[i] ) { // no symbol, check optional and alternative symbols @@ -226,14 +251,14 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { } if(!ok) { System.err.println("Fail: Could not resolve symbol <"+symbolNames[i]+">: not optional, no alternatives."); - return false; + res = false; } } else if(DEBUG) { System.err.println("OK: Unresolved optional symbol <"+symbolNames[i]+">"); } } } - return initSymbols0(symbolAddr, symbolCount); + return initSymbols0(symbolAddr, symbolCount) && res; } protected FFMPEGDynamicLibraryBundleInfo() { @@ -265,11 +290,13 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { final List<String> avutil = new ArrayList<String>(); avutil.add("avutil"); // default - avutil.add("libavutil.so.52"); // dummy future proof + avutil.add("libavutil.so.53"); // dummy future proof + avutil.add("libavutil.so.52"); // 9 avutil.add("libavutil.so.51"); // 0.8 avutil.add("libavutil.so.50"); // 0.7 - avutil.add("avutil-52"); // dummy future proof + avutil.add("avutil-53"); // dummy future proof + avutil.add("avutil-52"); // 9 avutil.add("avutil-51"); // 0.8 avutil.add("avutil-50"); // 0.7 libsList.add(avutil); @@ -278,12 +305,12 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { avformat.add("avformat"); // default avformat.add("libavformat.so.55"); // dummy future proof - avformat.add("libavformat.so.54"); // 0.? + avformat.add("libavformat.so.54"); // 9 avformat.add("libavformat.so.53"); // 0.8 avformat.add("libavformat.so.52"); // 0.7 avformat.add("avformat-55"); // dummy future proof - avformat.add("avformat-54"); // 0.? + avformat.add("avformat-54"); // 9 avformat.add("avformat-53"); // 0.8 avformat.add("avformat-52"); // 0.7 libsList.add(avformat); @@ -292,16 +319,26 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { avcodec.add("avcodec"); // default avcodec.add("libavcodec.so.55"); // dummy future proof - avcodec.add("libavcodec.so.54"); // 0.? + avcodec.add("libavcodec.so.54"); // 9 avcodec.add("libavcodec.so.53"); // 0.8 avcodec.add("libavcodec.so.52"); // 0.7 avcodec.add("avcodec-55"); // dummy future proof - avcodec.add("avcodec-54"); // 0.? + avcodec.add("avcodec-54"); // 9 avcodec.add("avcodec-53"); // 0.8 avcodec.add("avcodec-52"); // 0.7 libsList.add(avcodec); - + + final List<String> avresample = new ArrayList<String>(); + avresample.add("avresample"); // default + + avresample.add("libavresample.so.2"); // dummy future proof + avresample.add("libavresample.so.1"); // 9 + + avresample.add("avresample-2"); // dummy future proof + avresample.add("avresample-1"); // 9 + libsList.add(avresample); + return libsList; } diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java index 78f5954e5..80b946456 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java @@ -44,8 +44,7 @@ import com.jogamp.gluegen.runtime.ProcAddressTable; import com.jogamp.opengl.util.TimeFrameI; import com.jogamp.opengl.util.GLPixelStorageModes; import com.jogamp.opengl.util.av.AudioSink; -import com.jogamp.opengl.util.av.AudioSink.AudioDataFormat; -import com.jogamp.opengl.util.av.AudioSink.AudioDataType; +import com.jogamp.opengl.util.av.AudioSink.AudioFormat; import com.jogamp.opengl.util.av.AudioSinkFactory; import com.jogamp.opengl.util.av.GLMediaPlayer; import com.jogamp.opengl.util.texture.Texture; @@ -117,9 +116,11 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { private static final int avUtilMajorVersionCC; private static final int avFormatMajorVersionCC; private static final int avCodecMajorVersionCC; + private static final int avResampleMajorVersionCC; private static final VersionNumber avUtilVersion; private static final VersionNumber avFormatVersion; private static final VersionNumber avCodecVersion; + private static final VersionNumber avResampleVersion; private static final boolean available; static { @@ -129,15 +130,19 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { avUtilMajorVersionCC = getAvUtilMajorVersionCC0(); avFormatMajorVersionCC = getAvFormatMajorVersionCC0(); avCodecMajorVersionCC = getAvCodecMajorVersionCC0(); + avResampleMajorVersionCC = getAvResampleMajorVersionCC0(); avUtilVersion = getAVVersion(getAvUtilVersion0()); avFormatVersion = getAVVersion(getAvFormatVersion0()); avCodecVersion = getAVVersion(getAvCodecVersion0()); - System.err.println("LIB_AV Util : "+avUtilVersion+" [cc "+avUtilMajorVersionCC+"]"); - System.err.println("LIB_AV Format: "+avFormatVersion+" [cc "+avFormatMajorVersionCC+"]"); - System.err.println("LIB_AV Codec : "+avCodecVersion+" [cc "+avCodecMajorVersionCC+"]"); + avResampleVersion = getAVVersion(getAvResampleVersion0()); + System.err.println("LIB_AV Util : "+avUtilVersion+" [cc "+avUtilMajorVersionCC+"]"); + System.err.println("LIB_AV Format : "+avFormatVersion+" [cc "+avFormatMajorVersionCC+"]"); + System.err.println("LIB_AV Codec : "+avCodecVersion+" [cc "+avCodecMajorVersionCC+"]"); + System.err.println("LIB_AV Resample: "+avResampleVersion+" [cc "+avResampleMajorVersionCC+"]"); libAVVersionGood = avUtilMajorVersionCC == avUtilVersion.getMajor() && avFormatMajorVersionCC == avFormatVersion.getMajor() && - avCodecMajorVersionCC == avCodecVersion.getMajor(); + avCodecMajorVersionCC == avCodecVersion.getMajor() && + avResampleMajorVersionCC == avResampleVersion.getMajor(); if( !libAVVersionGood ) { System.err.println("LIB_AV Not Matching Compile-Time / Runtime Major-Version"); } @@ -145,9 +150,11 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { avUtilMajorVersionCC = 0; avFormatMajorVersionCC = 0; avCodecMajorVersionCC = 0; + avResampleMajorVersionCC = 0; avUtilVersion = null; avFormatVersion = null; avCodecVersion = null; + avResampleVersion = null; libAVVersionGood = false; } available = libAVGood && libAVVersionGood ? initIDs0() : false; @@ -185,9 +192,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { // Audio // - private SampleFormat aSampleFmt = null; - private AudioSink.AudioDataFormat avChosenAudioFormat; - private AudioSink.AudioDataFormat sinkChosenAudioFormat; + private AudioSink.AudioFormat avChosenAudioFormat; private int audioSamplesPerFrameAndChannel = 0; public FFMPEGMediaPlayer() { @@ -237,7 +242,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { } else { audioSink = AudioSinkFactory.createDefault(); } - final AudioDataFormat preferredAudioFormat = audioSink.getPreferredFormat(); + final AudioFormat preferredAudioFormat = audioSink.getPreferredFormat(); if(DEBUG) { System.err.println("initStream: p2 preferred "+preferredAudioFormat+", "+this); } @@ -274,7 +279,9 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { resStreamLocS = streamLocS; inFormat = null; } - setStream0(moviePtr, resStreamLocS, inFormat, vid, aid, snoopVideoFrameCount, preferredAudioFormat.channelCount, preferredAudioFormat.sampleRate); + final int aMaxChannelCount = audioSink.getMaxSupportedChannels(); + final int aPrefSampleRate = preferredAudioFormat.sampleRate; + setStream0(moviePtr, resStreamLocS, inFormat, vid, aid, snoopVideoFrameCount, aMaxChannelCount, aPrefSampleRate); } @Override @@ -308,20 +315,20 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { } else { frameDuration = AudioSink.DefaultFrameDuration; } - - sinkChosenAudioFormat = audioSink.init(avChosenAudioFormat, frameDuration, AudioSink.DefaultInitialQueueSize, AudioSink.DefaultQueueGrowAmount, audioQueueLimit); if(DEBUG) { - System.err.println("initGL: p3 avChosen "+avChosenAudioFormat+", chosen "+sinkChosenAudioFormat); + System.err.println("initGL: p3 avChosen "+avChosenAudioFormat); } - if( null == sinkChosenAudioFormat ) { + + final boolean audioSinkOK = audioSink.init(avChosenAudioFormat, frameDuration, AudioSink.DefaultInitialQueueSize, AudioSink.DefaultQueueGrowAmount, audioQueueLimit); + if( !audioSinkOK ) { System.err.println("AudioSink "+audioSink.getClass().getName()+" does not support "+avChosenAudioFormat+", using Null"); audioSink.destroy(); audioSink = AudioSinkFactory.createNull(); - sinkChosenAudioFormat = audioSink.init(avChosenAudioFormat, 0, AudioSink.DefaultInitialQueueSize, AudioSink.DefaultQueueGrowAmount, audioQueueLimit); + audioSink.init(avChosenAudioFormat, 0, AudioSink.DefaultInitialQueueSize, AudioSink.DefaultQueueGrowAmount, audioQueueLimit); } if(DEBUG) { - System.err.println("initGL: p4 chosen "+sinkChosenAudioFormat); - System.err.println("initGL: "+audioSink); + System.err.println("initGL: p4 chosen "+avChosenAudioFormat); + System.err.println("initGL: p4 chosen "+audioSink); } if( null != gl ) { @@ -350,6 +357,84 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { } /** + * @param sampleRate sample rate in Hz (1/s) + * @param sampleSize sample size in bits + * @param channelCount number of channels + * @param signed true if signed number, false for unsigned + * @param fixedP true for fixed point value, false for unsigned floating point value with a sampleSize of 32 (float) or 64 (double) + * @param planar true for planar data package (each channel in own data buffer), false for packed data channels interleaved in one buffer. + * @param littleEndian true for little-endian, false for big endian + * @return + */ + + /** + * Converts the given libav/ffmpeg values to {@link AudioFormat} and returns {@link AudioSink#isSupported(AudioFormat)}. + * @param audioSampleFmt ffmpeg/libav audio-sample-format, see {@link SampleFormat}. + * @param audioSampleRate sample rate in Hz (1/s) + * @param audioChannels number of channels + */ + private final boolean isAudioFormatSupported(int audioSampleFmt, int audioSampleRate, int audioChannels) { + final AudioFormat audioFormat = avAudioFormat2Local(SampleFormat.valueOf(audioSampleFmt), audioSampleRate, audioChannels); + final boolean res = audioSink.isSupported(audioFormat); + if( DEBUG ) { + System.err.println("AudioSink.isSupported: "+res+": "+audioFormat); + } + return res; + } + + /** + * Returns {@link AudioFormat} as converted from the given libav/ffmpeg values. + * @param audioSampleFmt ffmpeg/libav audio-sample-format, see {@link SampleFormat}. + * @param audioSampleRate sample rate in Hz (1/s) + * @param audioChannels number of channels + */ + private final AudioFormat avAudioFormat2Local(SampleFormat audioSampleFmt, int audioSampleRate, int audioChannels) { + final int sampleSize; + boolean planar = true; + final boolean signed, fixedP; + switch( audioSampleFmt ) { + case S32: + planar = false; + case S32P: + sampleSize = 32; + signed = true; + fixedP = true; + break; + case S16: + planar = false; + case S16P: + sampleSize = 16; + signed = true; + fixedP = true; + break; + case U8: + planar = false; + case U8P: + sampleSize = 8; + signed = false; + fixedP = true; + break; + case DBL: + planar = false; + case DBLP: + sampleSize = 64; + signed = true; + fixedP = true; + break; + case FLT: + planar = false; + case FLTP: + sampleSize = 32; + signed = true; + fixedP = true; + break; + default: // FIXME: Add more formats ! + throw new IllegalArgumentException("Unsupported sampleformat: "+audioSampleFmt); + } + return new AudioFormat(audioSampleRate, sampleSize, audioChannels, signed, fixedP, planar, true /* littleEndian */); + } + + /** * @param pixFmt * @param planes * @param bitsPerPixel @@ -400,44 +485,9 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { default: // FIXME: Add more formats ! throw new RuntimeException("Unsupported pixelformat: "+vPixelFmt); } - aSampleFmt = SampleFormat.valueOf(audioSampleFmt); - final int sampleSize; - final boolean signed, fixedP; - switch( aSampleFmt ) { - case S32: - case S32P: - sampleSize = 32; - signed = true; - fixedP = true; - break; - case S16: - case S16P: - sampleSize = 16; - signed = true; - fixedP = true; - break; - case U8: - case U8P: - sampleSize = 8; - signed = false; - fixedP = true; - break; - case DBL: - case DBLP: - sampleSize = 64; - signed = true; - fixedP = true; - break; - case FLT: - case FLTP: - sampleSize = 32; - signed = true; - fixedP = true; - break; - default: // FIXME: Add more formats ! - throw new RuntimeException("Unsupported sampleformat: "+aSampleFmt); - } - avChosenAudioFormat = new AudioDataFormat(AudioDataType.PCM, audioSampleRate, sampleSize, audioChannels, signed, fixedP, true /* littleEndian */); + final SampleFormat aSampleFmt = SampleFormat.valueOf(audioSampleFmt); + avChosenAudioFormat = avAudioFormat2Local(aSampleFmt, audioSampleRate, audioChannels); + this.audioSamplesPerFrameAndChannel = audioSamplesPerFrameAndChannel; if(DEBUG) { @@ -587,6 +637,8 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { private static native int getAvFormatMajorVersionCC0(); private static native int getAvCodecVersion0(); private static native int getAvCodecMajorVersionCC0(); + private static native int getAvResampleVersion0(); + private static native int getAvResampleMajorVersionCC0(); private static native boolean initIDs0(); private native long createInstance0(boolean verbose); private native void destroyInstance0(long moviePtr); @@ -595,7 +647,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { * Issues {@link #updateAttributes(int, int, int, int, int, int, int, float, int, int, String, String)} * and {@link #updateAttributes2(int, int, int, int, int, int, int, int, int, int)}. * <p> - * Always uses {@link AudioSink.AudioDataFormat}: + * Always uses {@link AudioSink.AudioFormat}: * <pre> * [type PCM, sampleRate [10000(?)..44100..48000], sampleSize 16, channelCount 1-2, signed, littleEndian] * </pre> @@ -607,10 +659,10 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { * @param aid * @param snoopVideoFrameCount snoop this number of video-frames to gather audio-frame-count per video-frame. * If zero, gathering audio-frame-count is disabled! - * @param aChannelCount - * @param aSampleRate + * @param aPrefChannelCount + * @param aPrefSampleRate */ - private native void setStream0(long moviePtr, String url, String inFormat, int vid, int aid, int snoopVideoFrameCount, int aChannelCount, int aSampleRate); + private native void setStream0(long moviePtr, String url, String inFormat, int vid, int aid, int snoopVideoFrameCount, int aMaxChannelCount, int aPrefSampleRate); private native void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish); private native int getVideoPTS0(long moviePtr); @@ -627,6 +679,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { private native int pause0(long moviePtr); private native int seek0(long moviePtr, int position); + /** FFMPEG/libAV Audio Sample Format */ public static enum SampleFormat { // NONE = -1, U8, ///< unsigned 8 bits @@ -653,6 +706,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { } }; + /** FFMPEG/libAV Pixel Format */ public static enum PixelFormat { // NONE= -1, YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) diff --git a/src/jogl/native/libav/ffmpeg_tool.h b/src/jogl/native/libav/ffmpeg_tool.h index 783746378..0a65723fe 100644 --- a/src/jogl/native/libav/ffmpeg_tool.h +++ b/src/jogl/native/libav/ffmpeg_tool.h @@ -43,6 +43,15 @@ #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> +#include <libavutil/avutil.h> +#if LIBAVCODEC_VERSION_MAJOR >= 54 +#include <libavresample/avresample.h> +#endif + +#ifndef LIBAVRESAMPLE_VERSION_MAJOR +#define LIBAVRESAMPLE_VERSION_MAJOR 0 +typedef void* AVAudioResampleContext; +#endif #include <stdarg.h> #include <stdio.h> @@ -85,6 +94,12 @@ typedef void (APIENTRYP PFNGLFINISH) (void); /** Since 55.0.0 */ #define AV_HAS_API_REFCOUNTED_FRAMES(pAV) (AV_VERSION_MAJOR(pAV->avcodecVersion) >= 55) +/** Since 54.0.0.1 */ +#define AV_HAS_API_AVRESAMPLE(pAV) (AV_VERSION_MAJOR(pAV->avresampleVersion) >= 1) + +#define MAX_INT(a,b) ( (a >= b) ? a : b ) +#define MIN_INT(a,b) ( (a <= b) ? a : b ) + static inline float my_av_q2f(AVRational a){ return a.num / (float) a.den; } @@ -112,6 +127,7 @@ typedef struct { uint32_t avcodecVersion; uint32_t avformatVersion; uint32_t avutilVersion; + uint32_t avresampleVersion; int32_t useRefCountedFrames; @@ -144,10 +160,16 @@ typedef struct { NIOBuffer_t* pANIOBuffers; int32_t aFrameCount; int32_t aFrameCurrent; - int32_t aSampleRate; - int32_t aChannels; int32_t aFrameSize; // in samples per channel! enum AVSampleFormat aSampleFmt; // native decoder fmt + int32_t aSampleRate; + int32_t aChannels; + int32_t aSinkSupport; // supported by AudioSink + AVAudioResampleContext *aResampleCtx; + uint8_t* aResampleBuffer; + enum AVSampleFormat aSampleFmtOut; // out fmt + int32_t aChannelsOut; + int32_t aSampleRateOut; int32_t aPTS; // msec - overall last audio PTS PTSStats aPTSStats; int32_t aFramesPerVideoFrame; // is 'snooped' diff --git a/src/jogl/native/libav/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c b/src/jogl/native/libav/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c index 7bf936113..514c60800 100644 --- a/src/jogl/native/libav/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c +++ b/src/jogl/native/libav/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c @@ -30,7 +30,6 @@ #include "JoglCommon.h" #include "ffmpeg_tool.h" -#include <libavutil/avutil.h> #include <libavutil/pixdesc.h> #include <libavutil/samplefmt.h> #if LIBAVUTIL_VERSION_MAJOR < 53 @@ -45,18 +44,20 @@ static jclass ffmpegMediaPlayerClazz = NULL; static jmethodID jni_mid_pushSound = NULL; static jmethodID jni_mid_updateAttributes1 = NULL; static jmethodID jni_mid_updateAttributes2 = NULL; +static jmethodID jni_mid_isAudioFormatSupported = NULL; #define HAS_FUNC(f) (NULL!=(f)) typedef unsigned (APIENTRYP AVCODEC_VERSION)(void); typedef unsigned (APIENTRYP AVUTIL_VERSION)(void); typedef unsigned (APIENTRYP AVFORMAT_VERSION)(void); +typedef unsigned (APIENTRYP AVRESAMPLE_VERSION)(void); static AVCODEC_VERSION sp_avcodec_version; static AVFORMAT_VERSION sp_avformat_version; static AVUTIL_VERSION sp_avutil_version; -// count: 3 - +static AVRESAMPLE_VERSION sp_avresample_version; +// count: 4 // libavcodec typedef int (APIENTRYP AVCODEC_CLOSE)(AVCodecContext *avctx); @@ -96,19 +97,23 @@ static AV_FREE_PACKET sp_av_free_packet; static AVCODEC_DECODE_AUDIO4 sp_avcodec_decode_audio4; // 53.25.0 static AVCODEC_DECODE_AUDIO3 sp_avcodec_decode_audio3; // 52.23.0 static AVCODEC_DECODE_VIDEO2 sp_avcodec_decode_video2; // 52.23.0 -// count: 21 +// count: 22 // libavutil typedef void (APIENTRYP AV_FRAME_UNREF)(AVFrame *frame); typedef void (APIENTRYP AV_FREE)(void *ptr); typedef int (APIENTRYP AV_GET_BITS_PER_PIXEL)(const AVPixFmtDescriptor *pixdesc); typedef int (APIENTRYP AV_SAMPLES_GET_BUFFER_SIZE)(int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align); +typedef int (APIENTRYP AV_GET_BYTES_PER_SAMPLE)(enum AVSampleFormat sample_fmt); +typedef int (APIENTRYP AV_OPT_SET_INT)(void *obj, const char *name, int64_t val, int search_flags); static const AVPixFmtDescriptor* sp_av_pix_fmt_descriptors; static AV_FRAME_UNREF sp_av_frame_unref; static AV_FREE sp_av_free; static AV_GET_BITS_PER_PIXEL sp_av_get_bits_per_pixel; static AV_SAMPLES_GET_BUFFER_SIZE sp_av_samples_get_buffer_size; -// count: 26 +static AV_GET_BYTES_PER_SAMPLE sp_av_get_bytes_per_sample; +static AV_OPT_SET_INT sp_av_opt_set_int; +// count: 28 // libavformat typedef AVFormatContext *(APIENTRYP AVFORMAT_ALLOC_CONTEXT)(void); @@ -146,9 +151,24 @@ static AVFORMAT_NETWORK_INIT sp_avformat_network_init; // 53.13.0 static AVFORMAT_NETWORK_DEINIT sp_avformat_network_deinit; // 53.13.0 static AVFORMAT_FIND_STREAM_INFO sp_avformat_find_stream_info; // 53.3.0 static AV_FIND_STREAM_INFO sp_av_find_stream_info; -// count: 43 - -#define SYMBOL_COUNT 43 +// count: 46 + +// libavresample [1.0.1] +typedef AVAudioResampleContext* (APIENTRYP AVRESAMPLE_ALLOC_CONTEXT)(void); // 1.0.1 +typedef int (APIENTRYP AVRESAMPLE_OPEN)(AVAudioResampleContext *avr); // 1.0.1 +typedef void (APIENTRYP AVRESAMPLE_CLOSE)(AVAudioResampleContext *avr); // 1.0.1 +typedef void (APIENTRYP AVRESAMPLE_FREE)(AVAudioResampleContext **avr); // 1.0.1 +typedef int (APIENTRYP AVRESAMPLE_CONVERT)(AVAudioResampleContext *avr, uint8_t **output, + int out_plane_size, int out_samples, uint8_t **input, + int in_plane_size, int in_samples); // 1.0.1 +static AVRESAMPLE_ALLOC_CONTEXT sp_avresample_alloc_context; +static AVRESAMPLE_OPEN sp_avresample_open; +static AVRESAMPLE_CLOSE sp_avresample_close; +static AVRESAMPLE_FREE sp_avresample_free; +static AVRESAMPLE_CONVERT sp_avresample_convert; +// count: 51 + +#define SYMBOL_COUNT 51 JNIEXPORT jboolean JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGDynamicLibraryBundleInfo_initSymbols0 (JNIEnv *env, jclass clazz, jobject jSymbols, jint count) @@ -169,7 +189,8 @@ JNIEXPORT jboolean JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGDynamicLibraryB sp_avcodec_version = (AVCODEC_VERSION) (intptr_t) symbols[i++]; sp_avformat_version = (AVFORMAT_VERSION) (intptr_t) symbols[i++]; sp_avutil_version = (AVUTIL_VERSION) (intptr_t) symbols[i++]; - // count: 3 + sp_avresample_version = (AVRESAMPLE_VERSION) (intptr_t) symbols[i++]; + // count: 4 sp_avcodec_close = (AVCODEC_CLOSE) (intptr_t) symbols[i++]; sp_avcodec_string = (AVCODEC_STRING) (intptr_t) symbols[i++]; @@ -189,14 +210,16 @@ JNIEXPORT jboolean JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGDynamicLibraryB sp_avcodec_decode_audio4 = (AVCODEC_DECODE_AUDIO4) (intptr_t) symbols[i++]; sp_avcodec_decode_audio3 = (AVCODEC_DECODE_AUDIO3) (intptr_t) symbols[i++]; sp_avcodec_decode_video2 = (AVCODEC_DECODE_VIDEO2) (intptr_t) symbols[i++]; - // count: 18 + // count: 22 sp_av_pix_fmt_descriptors = (const AVPixFmtDescriptor*) (intptr_t) symbols[i++]; sp_av_frame_unref = (AV_FRAME_UNREF) (intptr_t) symbols[i++]; sp_av_free = (AV_FREE) (intptr_t) symbols[i++]; sp_av_get_bits_per_pixel = (AV_GET_BITS_PER_PIXEL) (intptr_t) symbols[i++]; sp_av_samples_get_buffer_size = (AV_SAMPLES_GET_BUFFER_SIZE) (intptr_t) symbols[i++]; - // count: 22 + sp_av_get_bytes_per_sample = (AV_GET_BYTES_PER_SAMPLE) (intptr_t) symbols[i++]; + sp_av_opt_set_int = (AV_OPT_SET_INT) (intptr_t) symbols[i++]; + // count: 29 sp_avformat_alloc_context = (AVFORMAT_ALLOC_CONTEXT) (intptr_t) symbols[i++];; sp_avformat_free_context = (AVFORMAT_FREE_CONTEXT) (intptr_t) symbols[i++]; @@ -215,7 +238,14 @@ JNIEXPORT jboolean JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGDynamicLibraryB sp_avformat_network_deinit = (AVFORMAT_NETWORK_DEINIT) (intptr_t) symbols[i++]; sp_avformat_find_stream_info = (AVFORMAT_FIND_STREAM_INFO) (intptr_t) symbols[i++]; sp_av_find_stream_info = (AV_FIND_STREAM_INFO) (intptr_t) symbols[i++]; - // count: 38 + // count: 46 + + sp_avresample_alloc_context = (AVRESAMPLE_ALLOC_CONTEXT) (intptr_t) symbols[i++]; + sp_avresample_open = (AVRESAMPLE_OPEN) (intptr_t) symbols[i++]; + sp_avresample_close = (AVRESAMPLE_CLOSE) (intptr_t) symbols[i++]; + sp_avresample_free = (AVRESAMPLE_FREE) (intptr_t) symbols[i++]; + sp_avresample_convert = (AVRESAMPLE_CONVERT) (intptr_t) symbols[i++]; + // count: 51 (*env)->ReleasePrimitiveArrayCritical(env, jSymbols, symbols, 0); @@ -229,6 +259,10 @@ JNIEXPORT jboolean JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGDynamicLibraryB return JNI_TRUE; } +static int _isAudioFormatSupported(JNIEnv *env, jobject instance, enum AVSampleFormat aSampleFmt, int32_t aSampleRate, int32_t aChannels) +{ + return JNI_TRUE == (*env)->CallBooleanMethod(env, instance, jni_mid_isAudioFormatSupported, aSampleFmt, aSampleRate, aChannels); +} static void _updateJavaAttributes(JNIEnv *env, jobject instance, FFMPEGToolBasicAV_t* pAV) { // int shallBeDetached = 0; @@ -247,7 +281,7 @@ static void _updateJavaAttributes(JNIEnv *env, jobject instance, FFMPEGToolBasic pAV->vBitsPerPixel, pAV->vBytesPerPixelPerPlane, pAV->vLinesize[0], pAV->vLinesize[1], pAV->vLinesize[2], pAV->vTexWidth[0], pAV->vTexWidth[1], pAV->vTexWidth[2], h, - pAV->aFramesPerVideoFrame, pAV->aSampleFmt, pAV->aSampleRate, pAV->aChannels, pAV->aFrameSize); + pAV->aFramesPerVideoFrame, pAV->aSampleFmtOut, pAV->aSampleRateOut, pAV->aChannelsOut, pAV->aFrameSize); (*env)->CallVoidMethod(env, instance, jni_mid_updateAttributes1, pAV->vid, pAV->aid, w, h, @@ -261,6 +295,16 @@ static void _updateJavaAttributes(JNIEnv *env, jobject instance, FFMPEGToolBasic static void freeInstance(JNIEnv *env, FFMPEGToolBasicAV_t* pAV) { int i; if(NULL != pAV) { + // Close the A resampler + if( NULL != pAV->aResampleCtx ) { + sp_avresample_free(&pAV->aResampleCtx); + pAV->aResampleCtx = NULL; + } + if( NULL != pAV->aResampleBuffer ) { + sp_av_free(pAV->aResampleBuffer); + pAV->aResampleBuffer = NULL; + } + // Close the V codec if(NULL != pAV->pVCodecCtx) { sp_avcodec_close(pAV->pVCodecCtx); @@ -390,6 +434,19 @@ JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_getAvCo return (jint) LIBAVCODEC_VERSION_MAJOR; } +JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_getAvResampleVersion0 + (JNIEnv *env, jclass clazz) { + if(HAS_FUNC(sp_avresample_version)) { + return (jint) sp_avresample_version(); + } else { + return 0; + } +} +JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_getAvResampleMajorVersionCC0 + (JNIEnv *env, jclass clazz) { + return (jint) LIBAVRESAMPLE_VERSION_MAJOR; +} + JNIEXPORT jboolean JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_initIDs0 (JNIEnv *env, jclass clazz) { @@ -413,10 +470,12 @@ JNIEXPORT jboolean JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_ini jni_mid_pushSound = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "pushSound", "(Ljava/nio/ByteBuffer;II)V"); jni_mid_updateAttributes1 = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "updateAttributes", "(IIIIIIIFIIILjava/lang/String;Ljava/lang/String;)V"); jni_mid_updateAttributes2 = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "updateAttributes2", "(IIIIIIIIIIIIIIII)V"); + jni_mid_isAudioFormatSupported = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "isAudioFormatSupported", "(III)Z"); if(jni_mid_pushSound == NULL || jni_mid_updateAttributes1 == NULL || - jni_mid_updateAttributes2 == NULL) { + jni_mid_updateAttributes2 == NULL || + jni_mid_isAudioFormatSupported == NULL) { return JNI_FALSE; } return JNI_TRUE; @@ -433,6 +492,11 @@ JNIEXPORT jlong JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_create pAV->avcodecVersion = sp_avcodec_version(); pAV->avformatVersion = sp_avformat_version(); pAV->avutilVersion = sp_avutil_version(); + if(HAS_FUNC(sp_avresample_version)) { + pAV->avresampleVersion = sp_avresample_version(); + } else { + pAV->avresampleVersion = 0; + } #if LIBAVCODEC_VERSION_MAJOR >= 55 // TODO: We keep code on using 1 a/v frame per decoding cycle now. @@ -489,7 +553,7 @@ static int64_t evalPTS(PTSStats *ptsStats, int64_t inPTS, int64_t inDTS); JNIEXPORT void JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_setStream0 (JNIEnv *env, jobject instance, jlong ptr, jstring jURL, jstring jInFmtStr, jint vid, jint aid, - jint snoopVideoFrameCount, jint aChannelCount, jint aSampleRate) + jint snoopVideoFrameCount, jint aMaxChannelCount, jint aPrefSampleRate) { int res, i; jboolean iscopy; @@ -609,12 +673,15 @@ JNIEXPORT void JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_setStre pAV->pACodecCtx->workaround_bugs=FF_BUG_AUTODETECT; pAV->pACodecCtx->skip_frame=AVDISCARD_DEFAULT; - pAV->pACodecCtx->request_channel_layout=getDefaultAudioChannelLayout(aChannelCount); - if( AV_HAS_API_REQUEST_CHANNELS(pAV) && 1 <= aChannelCount && aChannelCount <= 2 ) { - pAV->pACodecCtx->request_channels=aChannelCount; - } + // Note: OpenAL well supports n-channel by now (SOFT), + // however - AFAIK AV_SAMPLE_FMT_S16 would allow no conversion! pAV->pACodecCtx->request_sample_fmt=AV_SAMPLE_FMT_S16; - // ignored: aSampleRate ! + if( 1 <= aMaxChannelCount && aMaxChannelCount <= 2 ) { + pAV->pACodecCtx->request_channel_layout=getDefaultAudioChannelLayout(aMaxChannelCount); + if( AV_HAS_API_REQUEST_CHANNELS(pAV) ) { + pAV->pACodecCtx->request_channels=aMaxChannelCount; + } + } pAV->pACodecCtx->skip_frame=AVDISCARD_DEFAULT; sp_avcodec_string(pAV->acodec, sizeof(pAV->acodec), pAV->pACodecCtx, 0); @@ -651,13 +718,66 @@ JNIEXPORT void JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_setStre pAV->aFrameSize = pAV->pACodecCtx->frame_size; // in samples per channel! pAV->aSampleFmt = pAV->pACodecCtx->sample_fmt; pAV->frames_audio = pAV->pAStream->nb_frames; + pAV->aSinkSupport = _isAudioFormatSupported(env, instance, pAV->aSampleFmt, pAV->aSampleRate, pAV->aChannels); if( pAV->verbose ) { - fprintf(stderr, "A channels %d [l %d], sample_rate %d, frame_size %d, frame_number %d, r_frame_rate %f, avg_frame_rate %f, nb_frames %d, [req_chan_layout %d, req_chan %d] \n", + fprintf(stderr, "A channels %d [l %d], sample_rate %d, frame_size %d, frame_number %d, r_frame_rate %f, avg_frame_rate %f, nb_frames %d, [maxChan %d, prefRate %d, req_chan_layout %d, req_chan %d], sink-support %d \n", pAV->aChannels, pAV->pACodecCtx->channel_layout, pAV->aSampleRate, pAV->aFrameSize, pAV->pACodecCtx->frame_number, my_av_q2f(pAV->pAStream->r_frame_rate), my_av_q2f(pAV->pAStream->avg_frame_rate), pAV->pAStream->nb_frames, - pAV->pACodecCtx->request_channel_layout, pAV->pACodecCtx->request_channels); + aMaxChannelCount, aPrefSampleRate, pAV->pACodecCtx->request_channel_layout, pAV->pACodecCtx->request_channels, + pAV->aSinkSupport); + } + + // default + pAV->aSampleFmtOut = pAV->aSampleFmt; + pAV->aChannelsOut = pAV->aChannels; + pAV->aSampleRateOut = pAV->aSampleRate; + + if( AV_HAS_API_AVRESAMPLE(pAV) && + ( pAV->aSampleFmt != AV_SAMPLE_FMT_S16 || + ( 0 != aPrefSampleRate && pAV->aSampleRate != aPrefSampleRate ) || + !pAV->aSinkSupport ) + ) { + if( 0 == aPrefSampleRate ) { + aPrefSampleRate = pAV->aSampleRate; + } + int32_t aSinkSupport = 0; + enum AVSampleFormat aSampleFmtOut = AV_SAMPLE_FMT_S16; + int32_t aChannelsOut; + int32_t aSampleRateOut; + int32_t minChannelCount = MIN_INT(aMaxChannelCount,pAV->pACodecCtx->channels); + + if( _isAudioFormatSupported(env, instance, aSampleFmtOut, aPrefSampleRate, pAV->pACodecCtx->channels) ) { + aChannelsOut = pAV->pACodecCtx->channels; + aSampleRateOut = aPrefSampleRate; + aSinkSupport = 1; + } else if( _isAudioFormatSupported(env, instance, aSampleFmtOut, aPrefSampleRate, minChannelCount) ) { + aChannelsOut = minChannelCount; + aSampleRateOut = aPrefSampleRate; + aSinkSupport = 1; + } + if( aSinkSupport ) { + pAV->aResampleCtx = sp_avresample_alloc_context(); + sp_av_opt_set_int(pAV->aResampleCtx, "in_channel_layout", pAV->pACodecCtx->channel_layout, 0); + sp_av_opt_set_int(pAV->aResampleCtx, "out_channel_layout", getDefaultAudioChannelLayout(aChannelsOut), 0); + sp_av_opt_set_int(pAV->aResampleCtx, "in_sample_rate", pAV->aSampleRate, 0); + sp_av_opt_set_int(pAV->aResampleCtx, "out_sample_rate", aSampleRateOut, 0); + sp_av_opt_set_int(pAV->aResampleCtx, "in_sample_fmt", pAV->aSampleFmt, 0); + sp_av_opt_set_int(pAV->aResampleCtx, "out_sample_fmt", aSampleFmtOut, 0); + + if ( sp_avresample_open(pAV->aResampleCtx) < 0 ) { + sp_avresample_free(&pAV->aResampleCtx); + pAV->aResampleCtx = NULL; + fprintf(stderr, "error initializing libavresample\n"); + } else { + // OK + pAV->aSampleFmtOut = aSampleFmtOut; + pAV->aChannelsOut = aChannelsOut; + pAV->aSampleRateOut = aSampleRateOut; + pAV->aSinkSupport = 1; + } + } } if( 0 >= snoopVideoFrameCount ) { @@ -961,7 +1081,7 @@ JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_readNex if( 0 == frameCount && AV_NOPTS_VALUE != pkt_pts ) { // 1st frame only, discard invalid PTS .. pAV->aPTS = my_av_q2i32( pkt_pts * 1000, time_base); } else { // subsequent frames or invalid PTS .. - const int32_t bytesPerSample = 2; // av_get_bytes_per_sample( pAV->pACodecCtx->sample_fmt ); + const int32_t bytesPerSample = sp_av_get_bytes_per_sample( pAV->pACodecCtx->sample_fmt ); pAV->aPTS += data_size / ( pAV->aChannels * bytesPerSample * ( pAV->aSampleRate / 1000 ) ); } if( pAV->verbose ) { @@ -972,9 +1092,46 @@ JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_readNex pAV->aFrameCurrent, pAV->aFrameCount, pAFrameCurrent, pAFrameCurrent->data[0], data_size); } if( NULL != env ) { + void* data_ptr = pAFrameCurrent->data[0]; // default + + if( NULL != pAV->aResampleCtx ) { + enum AVSampleFormat aSampleFmtOut; // out fmt + int32_t aChannelsOut; + int32_t aSampleRateOut; + + uint8_t *tmp_out; + int out_samples, out_size, out_linesize; + int osize = sp_av_get_bytes_per_sample( pAV->aSampleFmtOut ); + int nb_samples = pAFrameCurrent->nb_samples; + + out_size = sp_av_samples_get_buffer_size(&out_linesize, + pAV->aChannelsOut, + nb_samples, + pAV->aSampleFmtOut, 0 /* align */); + + tmp_out = av_realloc(pAV->aResampleBuffer, out_size); + if (!tmp_out) { + JoglCommon_throwNewRuntimeException(env, "Couldn't alloc resample buffer of size %d", out_size); + return; + } + pAV->aResampleBuffer = tmp_out; + + out_samples = sp_avresample_convert(pAV->aResampleCtx, + &pAV->aResampleBuffer, + out_linesize, nb_samples, + pAFrameCurrent->data, + pAFrameCurrent->linesize[0], + pAFrameCurrent->nb_samples); + if (out_samples < 0) { + JoglCommon_throwNewRuntimeException(env, "avresample_convert() failed"); + return; + } + data_size = out_samples * osize * pAV->aChannelsOut; + data_ptr = tmp_out; + } NIOBuffer_t * pNIOBufferCurrent = &pAV->pANIOBuffers[pAV->aFrameCurrent]; int newNIO = NULL == pNIOBufferCurrent->nioRef; - if( !newNIO && ( pAFrameCurrent->data[0] != pNIOBufferCurrent->origPtr || data_size > pNIOBufferCurrent->size ) ) { + if( !newNIO && ( data_ptr != pNIOBufferCurrent->origPtr || data_size > pNIOBufferCurrent->size ) ) { if(pAV->verbose) { fprintf(stderr, "A NIO: Free.0 ptr %p / ref %p, %d bytes\n", pNIOBufferCurrent->origPtr, pNIOBufferCurrent->nioRef, pNIOBufferCurrent->size); @@ -983,9 +1140,9 @@ JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_readNex newNIO = 1; } if( newNIO ) { - jobject jSampleData = (*env)->NewDirectByteBuffer(env, pAFrameCurrent->data[0], data_size); + jobject jSampleData = (*env)->NewDirectByteBuffer(env, data_ptr, data_size); pNIOBufferCurrent->nioRef = (*env)->NewGlobalRef(env, jSampleData); - pNIOBufferCurrent->origPtr = pAFrameCurrent->data[0]; + pNIOBufferCurrent->origPtr = data_ptr; pNIOBufferCurrent->size = data_size; if(pAV->verbose) { fprintf(stderr, "A NIO: Alloc ptr %p / ref %p, %d bytes\n", |