diff options
Diffstat (limited to 'src/jogl/native/libav')
-rw-r--r-- | src/jogl/native/libav/ffmpeg_dshow.c | 215 | ||||
-rw-r--r-- | src/jogl/native/libav/ffmpeg_dshow.h | 47 | ||||
-rw-r--r-- | src/jogl/native/libav/ffmpeg_impl_template.c | 1611 | ||||
-rw-r--r-- | src/jogl/native/libav/ffmpeg_lavc53_lavf53_lavu51.c | 33 | ||||
-rw-r--r-- | src/jogl/native/libav/ffmpeg_lavc54_lavf54_lavu52_lavr01.c | 33 | ||||
-rw-r--r-- | src/jogl/native/libav/ffmpeg_lavc55_lavf55_lavu52_lavr01.c | 33 | ||||
-rw-r--r-- | src/jogl/native/libav/ffmpeg_static.c | 91 | ||||
-rw-r--r-- | src/jogl/native/libav/ffmpeg_static.h | 50 | ||||
-rw-r--r-- | src/jogl/native/libav/ffmpeg_tool.h | 122 | ||||
-rw-r--r-- | src/jogl/native/libav/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c | 717 |
10 files changed, 2223 insertions, 729 deletions
diff --git a/src/jogl/native/libav/ffmpeg_dshow.c b/src/jogl/native/libav/ffmpeg_dshow.c new file mode 100644 index 000000000..4f1523a81 --- /dev/null +++ b/src/jogl/native/libav/ffmpeg_dshow.c @@ -0,0 +1,215 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +#include "ffmpeg_dshow.h" + +#ifdef _WIN32 + +#include <stdio.h> +#include <string.h> + +// dshow includes strsafe.h, hence tchar.h cannot be used +// include strsafe.h here for documentation +// #include <tchar.h> +#include <strsafe.h> +#include <dshow.h> + +static HRESULT EnumerateDevices(REFGUID category, IEnumMoniker **ppEnum) +{ + // Create the System Device Enumerator. + ICreateDevEnum *pDevEnum; + void *pv = NULL; + + HRESULT hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum, (void**)&pDevEnum); + + if (SUCCEEDED(hr)) { + // Create an enumerator for the category. + hr = pDevEnum->lpVtbl->CreateClassEnumerator(pDevEnum, category, ppEnum,0); + if (hr == S_FALSE) + { + hr = VFW_E_NOT_FOUND; // The category is empty. Treat as an error. + } + pDevEnum->lpVtbl->Release(pDevEnum); + } + return hr; +} + +static void getBSTRChars(BSTR bstr, char *pDest, int destLen) { + + #ifdef UNICODE + // _sntprintf(pDest, destLen, _T("%s"), bstr); + StringCbPrintfW(pDest, destLen, L"%s", bstr); + #else + // _sntprintf(pDest, destLen, _T("%S"), bstr); + StringCchPrintfA(pDest, destLen, "%S", bstr); + #endif +} + + +static int GetDeviceInformation(IEnumMoniker *pEnum, int verbose, int devIdx, + char *pDescr, int descrSize, + char *pName, int nameSize, + char *pPath, int pathSize, int *pWaveID) { + IMoniker *pMoniker = NULL; + int i=0; + int res = devIdx >= 0 ? -1 : 0; + + if( NULL != pDescr ) { + *pDescr=0; + } + if( NULL != pName ) { + *pName=0; + } + if( NULL != pPath ) { + *pPath=0; + } + if( NULL != pWaveID ) { + *pWaveID=0; + } + + while (pEnum->lpVtbl->Next(pEnum, 1, &pMoniker, NULL) == S_OK) { + IPropertyBag *pPropBag; + HRESULT hr; + + hr = pMoniker->lpVtbl->BindToStorage(pMoniker, 0, 0, &IID_IPropertyBag, (void**)&pPropBag); + if (FAILED(hr)) { + if( verbose ) { + fprintf(stderr, "DShowParser: Dev[%d]: bind failed ...\n", i); + } + pMoniker->lpVtbl->Release(pMoniker); + continue; + } + VARIANT var; + VariantInit(&var); + + // Get description or friendly name. + hr = pPropBag->lpVtbl->Read(pPropBag, L"Description", &var, 0); + if (SUCCEEDED(hr)) { + if( i == devIdx && NULL != pDescr ) { + res = 0; + getBSTRChars(var.bstrVal, pDescr, descrSize); + } + if( verbose ) { + fprintf(stderr, "DShowParser: Dev[%d]: Descr %S\n", i, var.bstrVal); + } + VariantClear(&var); + } else if( verbose ) { + fprintf(stderr, "DShowParser: Dev[%d]: cannot read Descr..\n", i); + } + + hr = pPropBag->lpVtbl->Read(pPropBag, L"FriendlyName", &var, 0); + if (SUCCEEDED(hr)) { + if( i == devIdx && NULL != pName ) { + res = 0; + getBSTRChars(var.bstrVal, pName, nameSize); + } + if( verbose ) { + fprintf(stderr, "DShowParser: Dev[%d]: Name %S\n", i, var.bstrVal); + } + VariantClear(&var); + } else if( verbose ) { + fprintf(stderr, "DShowParser: Dev[%d]: cannot read Name..\n", i); + } + + hr = pPropBag->lpVtbl->Write(pPropBag, L"FriendlyName", &var); + + // WaveInID applies only to audio capture devices. + hr = pPropBag->lpVtbl->Read(pPropBag, L"WaveInID", &var, 0); + if (SUCCEEDED(hr)) { + if( i == devIdx && NULL != pWaveID ) { + res = 0; + *pWaveID=(int)var.lVal; + } + if( verbose ) { + fprintf(stderr, "DShowParser: Dev[%d]: WaveInID %d\n", i, var.lVal); + } + VariantClear(&var); + } + + hr = pPropBag->lpVtbl->Read(pPropBag, L"DevicePath", &var, 0); + if (SUCCEEDED(hr)) { + if( i == devIdx && NULL != pPath ) { + res = 0; + getBSTRChars(var.bstrVal, pPath, pathSize); + } + if( verbose ) { + fprintf(stderr, "DShowParser: Dev[%d]: Path %S\n", i, var.bstrVal); + } + VariantClear(&var); + } + + pPropBag->lpVtbl->Release(pPropBag); + pMoniker->lpVtbl->Release(pMoniker); + + if( devIdx >= 0 && i == devIdx ) { + break; // done! + } + i++; + } + return res; +} + +int findDShowVideoDevice(char * dest, int destSize, int devIdx, int verbose) { + int res = -1; + + HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (SUCCEEDED(hr)) { + IEnumMoniker *pEnum; + + hr = EnumerateDevices(&CLSID_VideoInputDeviceCategory, &pEnum); + if (SUCCEEDED(hr)) { + res = GetDeviceInformation(pEnum, verbose, devIdx, NULL /* pDescr */, 0, dest, destSize, NULL /* pPath */, 0, NULL /* pWaveID */); + pEnum->lpVtbl->Release(pEnum); + if( verbose ) { + fprintf(stderr, "DShowParser: Get VideoInputDevice: res %d, '%s'\n", res, dest); + } + } else if( verbose ) { + fprintf(stderr, "DShowParser: Get VideoInputDevice failed\n"); + } + /** + hr = EnumerateDevices(&CLSID_AudioInputDeviceCategory, &pEnum); + if (SUCCEEDED(hr)) { + res = GetDeviceInformation(pEnum, verbose, devIdx, NULL, 0, NULL, 0, NULL, 0, NULL); + pEnum->lpVtbl->Release(pEnum); + } else if( verbose ) { + fprintf(stderr, "DShowParser: Get AudioInputDevice failed\n"); + } */ + CoUninitialize(); + } else if( verbose ) { + fprintf(stderr, "DShowParser: CoInit failed\n"); + } + return res; +} + +#else + +int findDShowVideoDevice(char * dest, int destSize, int devIdx, int verbose) { + return -1; +} + +#endif diff --git a/src/jogl/native/libav/ffmpeg_dshow.h b/src/jogl/native/libav/ffmpeg_dshow.h new file mode 100644 index 000000000..44524bf4b --- /dev/null +++ b/src/jogl/native/libav/ffmpeg_dshow.h @@ -0,0 +1,47 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +#ifndef _FFMPEG_DSHOW_H +#define _FFMPEG_DSHOW_H + +#ifdef _WIN32 + +#include <windows.h> + +#endif // _WIN32 + +#include <gluegen_stdint.h> +#include <gluegen_inttypes.h> +#include <gluegen_stddef.h> +#include <gluegen_stdint.h> + +extern int findDShowVideoDevice(char * dest, int destSize, int devIdx, int verbose); + + +#endif // _FFMPEG_DSHOW_H + diff --git a/src/jogl/native/libav/ffmpeg_impl_template.c b/src/jogl/native/libav/ffmpeg_impl_template.c new file mode 100644 index 000000000..a7e9f4857 --- /dev/null +++ b/src/jogl/native/libav/ffmpeg_impl_template.c @@ -0,0 +1,1611 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +// #define FF_FUNC(METHOD) Java_jogamp_opengl_util_av_impl_FFMPEGv08 ## METHOD + +#include "JoglCommon.h" +#include "ffmpeg_tool.h" +#include "ffmpeg_static.h" +#include "ffmpeg_dshow.h" + +#include "libavutil/pixdesc.h" +#include "libavutil/samplefmt.h" +#if LIBAVUTIL_VERSION_MAJOR < 53 + #include "libavutil/audioconvert.h" + // 52: #include "libavutil/channel_layout.h" +#endif + +#include <GL/gl.h> + +#define HAS_FUNC(f) (NULL!=(f)) + +typedef unsigned (APIENTRYP AVUTIL_VERSION)(void); +typedef unsigned (APIENTRYP AVFORMAT_VERSION)(void); +typedef unsigned (APIENTRYP AVCODEC_VERSION)(void); +typedef unsigned (APIENTRYP AVRESAMPLE_VERSION)(void); +typedef unsigned (APIENTRYP SWRESAMPLE_VERSION)(void); + +static AVUTIL_VERSION sp_avutil_version; +static AVFORMAT_VERSION sp_avformat_version; +static AVCODEC_VERSION sp_avcodec_version; +static AVRESAMPLE_VERSION sp_avresample_version; +static SWRESAMPLE_VERSION sp_swresample_version; +// count: 5 + +// libavcodec +typedef int (APIENTRYP AVCODEC_REGISTER_ALL)(void); +typedef int (APIENTRYP AVCODEC_CLOSE)(AVCodecContext *avctx); +typedef void (APIENTRYP AVCODEC_STRING)(char *buf, int buf_size, AVCodecContext *enc, int encode); +typedef AVCodec *(APIENTRYP AVCODEC_FIND_DECODER)(int avCodecID); // lavc 53: 'enum CodecID id', lavc 54: 'enum AVCodecID id' +typedef int (APIENTRYP AVCODEC_OPEN2)(AVCodecContext *avctx, AVCodec *codec, AVDictionary **options); // 53.6.0 +typedef AVFrame *(APIENTRYP AVCODEC_ALLOC_FRAME)(void); +typedef void (APIENTRYP AVCODEC_GET_FRAME_DEFAULTS)(AVFrame *frame); +typedef void (APIENTRYP AVCODEC_FREE_FRAME)(AVFrame **frame); +typedef int (APIENTRYP AVCODEC_DEFAULT_GET_BUFFER)(AVCodecContext *s, AVFrame *pic); // <= 54 (opt), else AVCODEC_DEFAULT_GET_BUFFER2 +typedef void (APIENTRYP AVCODEC_DEFAULT_RELEASE_BUFFER)(AVCodecContext *s, AVFrame *pic); // <= 54 (opt), else AV_FRAME_UNREF +typedef int (APIENTRYP AVCODEC_DEFAULT_GET_BUFFER2)(AVCodecContext *s, AVFrame *frame, int flags); // 55. (opt) +typedef int (APIENTRYP AVCODEC_GET_EDGE_WIDTH)(); +typedef int (APIENTRYP AV_IMAGE_FILL_LINESIZES)(int linesizes[4], int pix_fmt, int width); // lavu 51: 'enum PixelFormat pix_fmt', lavu 53: 'enum AVPixelFormat pix_fmt' +typedef void (APIENTRYP AVCODEC_ALIGN_DIMENSIONS)(AVCodecContext *s, int *width, int *height); +typedef void (APIENTRYP AVCODEC_ALIGN_DIMENSIONS2)(AVCodecContext *s, int *width, int *height, int linesize_align[AV_NUM_DATA_POINTERS]); +typedef void (APIENTRYP AVCODEC_FLUSH_BUFFERS)(AVCodecContext *avctx); +typedef void (APIENTRYP AV_INIT_PACKET)(AVPacket *pkt); +typedef int (APIENTRYP AV_NEW_PACKET)(AVPacket *pkt, int size); +typedef void (APIENTRYP AV_DESTRUCT_PACKET)(AVPacket *pkt); +typedef void (APIENTRYP AV_FREE_PACKET)(AVPacket *pkt); +typedef int (APIENTRYP AVCODEC_DECODE_AUDIO4)(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, AVPacket *avpkt); // 53.25.0 +typedef int (APIENTRYP AVCODEC_DECODE_VIDEO2)(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, AVPacket *avpkt); // 52.23.0 + +static AVCODEC_REGISTER_ALL sp_avcodec_register_all; +static AVCODEC_CLOSE sp_avcodec_close; +static AVCODEC_STRING sp_avcodec_string; +static AVCODEC_FIND_DECODER sp_avcodec_find_decoder; +static AVCODEC_OPEN2 sp_avcodec_open2; // 53.6.0 +static AVCODEC_ALLOC_FRAME sp_avcodec_alloc_frame; +static AVCODEC_GET_FRAME_DEFAULTS sp_avcodec_get_frame_defaults; +static AVCODEC_FREE_FRAME sp_avcodec_free_frame; +static AVCODEC_DEFAULT_GET_BUFFER sp_avcodec_default_get_buffer; // <= 54 (opt), else sp_avcodec_default_get_buffer2 +static AVCODEC_DEFAULT_RELEASE_BUFFER sp_avcodec_default_release_buffer; // <= 54 (opt), else sp_av_frame_unref +static AVCODEC_DEFAULT_GET_BUFFER2 sp_avcodec_default_get_buffer2; // 55. (opt) +static AVCODEC_GET_EDGE_WIDTH sp_avcodec_get_edge_width; +static AV_IMAGE_FILL_LINESIZES sp_av_image_fill_linesizes; +static AVCODEC_ALIGN_DIMENSIONS sp_avcodec_align_dimensions; +static AVCODEC_ALIGN_DIMENSIONS2 sp_avcodec_align_dimensions2; +static AVCODEC_FLUSH_BUFFERS sp_avcodec_flush_buffers; +static AV_INIT_PACKET sp_av_init_packet; +static AV_NEW_PACKET sp_av_new_packet; +static AV_DESTRUCT_PACKET sp_av_destruct_packet; +static AV_FREE_PACKET sp_av_free_packet; +static AVCODEC_DECODE_AUDIO4 sp_avcodec_decode_audio4; // 53.25.0 +static AVCODEC_DECODE_VIDEO2 sp_avcodec_decode_video2; // 52.23.0 +// count: 27 + +// libavutil +typedef void (APIENTRYP AV_FRAME_UNREF)(AVFrame *frame); +typedef void* (APIENTRYP AV_REALLOC)(void *ptr, size_t size); +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); +typedef AVDictionaryEntry* (APIENTRYP AV_DICT_GET)(AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags); +typedef int (APIENTRYP AV_DICT_COUNT)(AVDictionary *m); +typedef int (APIENTRYP AV_DICT_SET)(AVDictionary **pm, const char *key, const char *value, int flags); +typedef void (APIENTRYP AV_DICT_FREE)(AVDictionary **m); + +static const AVPixFmtDescriptor* sp_av_pix_fmt_descriptors; +static AV_FRAME_UNREF sp_av_frame_unref; +static AV_REALLOC sp_av_realloc; +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; +static AV_GET_BYTES_PER_SAMPLE sp_av_get_bytes_per_sample; +static AV_OPT_SET_INT sp_av_opt_set_int; +static AV_DICT_GET sp_av_dict_get; +static AV_DICT_COUNT sp_av_dict_count; +static AV_DICT_SET sp_av_dict_set; +static AV_DICT_FREE sp_av_dict_free; +// count: 39 + +// libavformat +typedef AVFormatContext *(APIENTRYP AVFORMAT_ALLOC_CONTEXT)(void); +typedef void (APIENTRYP AVFORMAT_FREE_CONTEXT)(AVFormatContext *s); // 52.96.0 +typedef void (APIENTRYP AVFORMAT_CLOSE_INPUT)(AVFormatContext **s); // 53.17.0 +typedef void (APIENTRYP AV_REGISTER_ALL)(void); +typedef AVInputFormat *(APIENTRYP AV_FIND_INPUT_FORMAT)(const char *short_name); +typedef int (APIENTRYP AVFORMAT_OPEN_INPUT)(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options); +typedef void (APIENTRYP AV_DUMP_FORMAT)(AVFormatContext *ic, int index, const char *url, int is_output); +typedef int (APIENTRYP AV_READ_FRAME)(AVFormatContext *s, AVPacket *pkt); +typedef int (APIENTRYP AV_SEEK_FRAME)(AVFormatContext *s, int stream_index, int64_t timestamp, int flags); +typedef int (APIENTRYP AVFORMAT_SEEK_FILE)(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); +typedef int (APIENTRYP AV_READ_PLAY)(AVFormatContext *s); +typedef int (APIENTRYP AV_READ_PAUSE)(AVFormatContext *s); +typedef int (APIENTRYP AVFORMAT_NETWORK_INIT)(void); // 53.13.0 +typedef int (APIENTRYP AVFORMAT_NETWORK_DEINIT)(void); // 53.13.0 +typedef int (APIENTRYP AVFORMAT_FIND_STREAM_INFO)(AVFormatContext *ic, AVDictionary **options); // 53.3.0 + +static AVFORMAT_ALLOC_CONTEXT sp_avformat_alloc_context; +static AVFORMAT_FREE_CONTEXT sp_avformat_free_context; // 52.96.0 (not used, only for outfile cts) +static AVFORMAT_CLOSE_INPUT sp_avformat_close_input; // 53.17.0 +static AV_REGISTER_ALL sp_av_register_all; +static AV_FIND_INPUT_FORMAT sp_av_find_input_format; +static AVFORMAT_OPEN_INPUT sp_avformat_open_input; +static AV_DUMP_FORMAT sp_av_dump_format; +static AV_READ_FRAME sp_av_read_frame; +static AV_SEEK_FRAME sp_av_seek_frame; +static AVFORMAT_SEEK_FILE sp_avformat_seek_file; +static AV_READ_PLAY sp_av_read_play; +static AV_READ_PAUSE sp_av_read_pause; +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 +// count: 54 + +// libavdevice [53.0.0] +typedef int (APIENTRYP AVDEVICE_REGISTER_ALL)(void); +static AVDEVICE_REGISTER_ALL sp_avdevice_register_all; +// count: 55 + +// 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: 60 + +// libswresample [1...] +typedef int (APIENTRYP AV_OPT_SET_SAMPLE_FMT)(void *obj, const char *name, enum AVSampleFormat fmt, int search_flags); // actually lavu .. but exist only w/ swresample! +typedef struct SwrContext *(APIENTRYP SWR_ALLOC)(void); +typedef int (APIENTRYP SWR_INIT)(struct SwrContext *s); +typedef void (APIENTRYP SWR_FREE)(struct SwrContext **s); +typedef int (APIENTRYP SWR_CONVERT)(struct SwrContext *s, uint8_t **out, int out_count, const uint8_t **in , int in_count); + +static AV_OPT_SET_SAMPLE_FMT sp_av_opt_set_sample_fmt; +static SWR_ALLOC sp_swr_alloc; +static SWR_INIT sp_swr_init; +static SWR_FREE sp_swr_free; +static SWR_CONVERT sp_swr_convert; +// count: 65 + +// We use JNI Monitor Locking, since this removes the need +// to statically link-in pthreads on window .. +// #define USE_PTHREAD_LOCKING 1 +// +#define USE_JNI_LOCKING 1 + +#if defined (USE_PTHREAD_LOCKING) + #include <pthread.h> + #warning USE LOCKING PTHREAD + static pthread_mutex_t mutex_avcodec_openclose; + #define MY_MUTEX_LOCK(e,s) pthread_mutex_lock(&(s)) + #define MY_MUTEX_UNLOCK(e,s) pthread_mutex_unlock(&(s)) +#elif defined (USE_JNI_LOCKING) + static jobject mutex_avcodec_openclose; + #define MY_MUTEX_LOCK(e,s) (*e)->MonitorEnter(e, s) + #define MY_MUTEX_UNLOCK(e,s) (*e)->MonitorExit(e, s) +#else + #warning USE LOCKING NONE + #define MY_MUTEX_LOCK(e,s) + #define MY_MUTEX_UNLOCK(e,s) +#endif + +#define SYMBOL_COUNT 65 + +JNIEXPORT jboolean JNICALL FF_FUNC(initSymbols0) + (JNIEnv *env, jobject instance, jobject jmutex_avcodec_openclose, jobject jSymbols, jint count) +{ + #ifdef USE_PTHREAD_LOCKING + pthread_mutexattr_t renderLockAttr; + #endif + int64_t* symbols; // jlong -> int64_t -> intptr_t -> FUNC_PTR + int i; + + if(SYMBOL_COUNT != count) { + fprintf(stderr, "FFMPEGNatives.initSymbols0: Wrong symbol count: Expected %d, Is %d\n", + SYMBOL_COUNT, count); + return JNI_FALSE; + } + JoglCommon_init(env); + + i = 0; + symbols = (int64_t *) (*env)->GetPrimitiveArrayCritical(env, jSymbols, NULL); + + sp_avutil_version = (AVUTIL_VERSION) (intptr_t) symbols[i++]; + sp_avformat_version = (AVFORMAT_VERSION) (intptr_t) symbols[i++]; + sp_avcodec_version = (AVCODEC_VERSION) (intptr_t) symbols[i++]; + sp_avresample_version = (AVRESAMPLE_VERSION) (intptr_t) symbols[i++]; + sp_swresample_version = (SWRESAMPLE_VERSION) (intptr_t) symbols[i++]; + + sp_avcodec_register_all = (AVCODEC_REGISTER_ALL) (intptr_t) symbols[i++]; + sp_avcodec_close = (AVCODEC_CLOSE) (intptr_t) symbols[i++]; + sp_avcodec_string = (AVCODEC_STRING) (intptr_t) symbols[i++]; + sp_avcodec_find_decoder = (AVCODEC_FIND_DECODER) (intptr_t) symbols[i++]; + sp_avcodec_open2 = (AVCODEC_OPEN2) (intptr_t) symbols[i++]; + sp_avcodec_alloc_frame = (AVCODEC_ALLOC_FRAME) (intptr_t) symbols[i++]; + sp_avcodec_get_frame_defaults = (AVCODEC_GET_FRAME_DEFAULTS) (intptr_t) symbols[i++]; + sp_avcodec_free_frame = (AVCODEC_FREE_FRAME) (intptr_t) symbols[i++]; + sp_avcodec_default_get_buffer = (AVCODEC_DEFAULT_GET_BUFFER) (intptr_t) symbols[i++]; + sp_avcodec_default_release_buffer = (AVCODEC_DEFAULT_RELEASE_BUFFER) (intptr_t) symbols[i++]; + sp_avcodec_default_get_buffer2 = (AVCODEC_DEFAULT_GET_BUFFER2) (intptr_t) symbols[i++]; + sp_avcodec_get_edge_width = (AVCODEC_GET_EDGE_WIDTH) (intptr_t) symbols[i++]; + sp_av_image_fill_linesizes = (AV_IMAGE_FILL_LINESIZES) (intptr_t) symbols[i++]; + sp_avcodec_align_dimensions = (AVCODEC_ALIGN_DIMENSIONS) (intptr_t) symbols[i++]; + sp_avcodec_align_dimensions2 = (AVCODEC_ALIGN_DIMENSIONS2) (intptr_t) symbols[i++]; + sp_avcodec_flush_buffers = (AVCODEC_FLUSH_BUFFERS) (intptr_t) symbols[i++]; + sp_av_init_packet = (AV_INIT_PACKET) (intptr_t) symbols[i++]; + sp_av_new_packet = (AV_NEW_PACKET) (intptr_t) symbols[i++]; + sp_av_destruct_packet = (AV_DESTRUCT_PACKET) (intptr_t) symbols[i++]; + sp_av_free_packet = (AV_FREE_PACKET) (intptr_t) symbols[i++]; + sp_avcodec_decode_audio4 = (AVCODEC_DECODE_AUDIO4) (intptr_t) symbols[i++]; + sp_avcodec_decode_video2 = (AVCODEC_DECODE_VIDEO2) (intptr_t) symbols[i++]; + + sp_av_pix_fmt_descriptors = (const AVPixFmtDescriptor*) (intptr_t) symbols[i++]; + sp_av_frame_unref = (AV_FRAME_UNREF) (intptr_t) symbols[i++]; + sp_av_realloc = (AV_REALLOC) (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++]; + 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++]; + sp_av_dict_get = (AV_DICT_GET) (intptr_t) symbols[i++]; + sp_av_dict_count = (AV_DICT_COUNT) (intptr_t) symbols[i++]; + sp_av_dict_set = (AV_DICT_SET) (intptr_t) symbols[i++]; + sp_av_dict_free = (AV_DICT_FREE) (intptr_t) symbols[i++]; + + sp_avformat_alloc_context = (AVFORMAT_ALLOC_CONTEXT) (intptr_t) symbols[i++];; + sp_avformat_free_context = (AVFORMAT_FREE_CONTEXT) (intptr_t) symbols[i++]; + sp_avformat_close_input = (AVFORMAT_CLOSE_INPUT) (intptr_t) symbols[i++]; + sp_av_register_all = (AV_REGISTER_ALL) (intptr_t) symbols[i++]; + sp_av_find_input_format = (AV_FIND_INPUT_FORMAT) (intptr_t) symbols[i++]; + sp_avformat_open_input = (AVFORMAT_OPEN_INPUT) (intptr_t) symbols[i++]; + sp_av_dump_format = (AV_DUMP_FORMAT) (intptr_t) symbols[i++]; + sp_av_read_frame = (AV_READ_FRAME) (intptr_t) symbols[i++]; + sp_av_seek_frame = (AV_SEEK_FRAME) (intptr_t) symbols[i++]; + sp_avformat_seek_file = (AVFORMAT_SEEK_FILE) (intptr_t) symbols[i++]; + sp_av_read_play = (AV_READ_PLAY) (intptr_t) symbols[i++]; + sp_av_read_pause = (AV_READ_PAUSE) (intptr_t) symbols[i++]; + sp_avformat_network_init = (AVFORMAT_NETWORK_INIT) (intptr_t) symbols[i++]; + 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_avdevice_register_all = (AVDEVICE_REGISTER_ALL) (intptr_t) symbols[i++]; + + 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++]; + + sp_av_opt_set_sample_fmt = (AV_OPT_SET_SAMPLE_FMT) (intptr_t) symbols[i++]; + sp_swr_alloc = (SWR_ALLOC) (intptr_t) symbols[i++]; + sp_swr_init = (SWR_INIT) (intptr_t) symbols[i++]; + sp_swr_free = (SWR_FREE) (intptr_t) symbols[i++]; + sp_swr_convert = (SWR_CONVERT) (intptr_t) symbols[i++]; + + (*env)->ReleasePrimitiveArrayCritical(env, jSymbols, symbols, 0); + + if(SYMBOL_COUNT != i) { + // boom + fprintf(stderr, "FFMPEGNatives.initSymbols0: Wrong symbol assignment count: Expected %d, Is %d\n", + SYMBOL_COUNT, i); + return JNI_FALSE; + } + + #if LIBAVCODEC_VERSION_MAJOR >= 55 + if(!HAS_FUNC(sp_avcodec_default_get_buffer2) || + !HAS_FUNC(sp_av_frame_unref) ) { + fprintf(stderr, "avcodec >= 55: avcodec_default_get_buffer2 %p, av_frame_unref %p\n", + sp_avcodec_default_get_buffer2, sp_av_frame_unref); + return JNI_FALSE; + } + #else + if(!HAS_FUNC(sp_avcodec_default_get_buffer) || + !HAS_FUNC(sp_avcodec_default_release_buffer)) { + fprintf(stderr, "avcodec < 55: avcodec_default_get_buffer %p, sp_avcodec_default_release_buffer %p\n", + sp_avcodec_default_get_buffer2, sp_avcodec_default_release_buffer); + return JNI_FALSE; + } + #endif + + #if defined (USE_PTHREAD_LOCKING) + pthread_mutexattr_init(&renderLockAttr); + pthread_mutexattr_settype(&renderLockAttr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex_avcodec_openclose, &renderLockAttr); // recursive + #elif defined (USE_JNI_LOCKING) + mutex_avcodec_openclose = (*env)->NewGlobalRef(env, jmutex_avcodec_openclose); + #endif + + /** At static destroy: Never + #if defined (USE_PTHREAD_LOCKING) + pthread_mutex_unlock(&mutex_avcodec_openclose); + pthread_mutex_destroy(&mutex_avcodec_openclose); + #elif defined (USE_JNI_LOCKING) + (*env)->DeleteGlobalRef(env, mutex_avcodec_openclose); + #endif + */ + + return JNI_TRUE; +} + +static int _isAudioFormatSupported(JNIEnv *env, jobject ffmpegMediaPlayer, enum AVSampleFormat aSampleFmt, int32_t aSampleRate, int32_t aChannels) { + return JNI_TRUE == (*env)->CallBooleanMethod(env, ffmpegMediaPlayer, ffmpeg_jni_mid_isAudioFormatSupported, aSampleFmt, aSampleRate, aChannels); +} +static void _updateJavaAttributes(JNIEnv *env, FFMPEGToolBasicAV_t* pAV) { + if(NULL!=env) { + (*env)->CallVoidMethod(env, pAV->ffmpegMediaPlayer, ffmpeg_jni_mid_setupFFAttributes, + pAV->vid, pAV->vPixFmt, pAV->vBufferPlanes, + pAV->vBitsPerPixel, pAV->vBytesPerPixelPerPlane, + pAV->vTexWidth[0], pAV->vTexWidth[1], pAV->vTexWidth[2], + pAV->vWidth, pAV->vHeight, + pAV->aid, pAV->aSampleFmtOut, pAV->aSampleRateOut, pAV->aChannelsOut, pAV->aFrameSize); + (*env)->CallVoidMethod(env, pAV->ffmpegMediaPlayer, ffmpeg_jni_mid_updateAttributes, + pAV->vid, pAV->aid, + pAV->vWidth, pAV->vHeight, + pAV->bps_stream, pAV->bps_video, pAV->bps_audio, + pAV->fps, pAV->frames_video, pAV->frames_audio, pAV->duration, + (*env)->NewStringUTF(env, pAV->vcodec), + (*env)->NewStringUTF(env, pAV->acodec) ); + } +} +static void _setIsGLOriented(JNIEnv *env, FFMPEGToolBasicAV_t* pAV) { + if(NULL!=env) { + (*env)->CallVoidMethod(env, pAV->ffmpegMediaPlayer, ffmpeg_jni_mid_setIsGLOriented, pAV->vFlipped); + } +} + +static void freeInstance(JNIEnv *env, FFMPEGToolBasicAV_t* pAV) { + int i; + if(NULL != pAV) { + // Close the A resampler + if( NULL != pAV->avResampleCtx ) { + sp_avresample_free(&pAV->avResampleCtx); + pAV->avResampleCtx = NULL; + } + if( NULL != pAV->swResampleCtx ) { + sp_swr_free(&pAV->swResampleCtx); + pAV->swResampleCtx = NULL; + } + if( NULL != pAV->aResampleBuffer ) { + sp_av_free(pAV->aResampleBuffer); + pAV->aResampleBuffer = NULL; + } + + MY_MUTEX_LOCK(env, mutex_avcodec_openclose); + { + // Close the V codec + if(NULL != pAV->pVCodecCtx) { + sp_avcodec_close(pAV->pVCodecCtx); + pAV->pVCodecCtx = NULL; + } + pAV->pVCodec=NULL; + + // Close the A codec + if(NULL != pAV->pACodecCtx) { + sp_avcodec_close(pAV->pACodecCtx); + pAV->pACodecCtx = NULL; + } + pAV->pACodec=NULL; + } + MY_MUTEX_UNLOCK(env, mutex_avcodec_openclose); + + // Close the frames + if(NULL != pAV->pVFrame) { + if(HAS_FUNC(sp_avcodec_free_frame)) { + sp_avcodec_free_frame(&pAV->pVFrame); + } else { + sp_av_free(pAV->pVFrame); + } + pAV->pVFrame = NULL; + } + if(NULL != pAV->pANIOBuffers) { + for(i=0; i<pAV->aFrameCount; i++) { + NIOBuffer_t * pNIOBuffer = &pAV->pANIOBuffers[i]; + if( NULL != pNIOBuffer->nioRef ) { + if(pAV->verbose) { + fprintf(stderr, "A NIO: Free.X ptr %p / ref %p, %d bytes\n", + pNIOBuffer->origPtr, pNIOBuffer->nioRef, pNIOBuffer->size); + } + (*env)->DeleteGlobalRef(env, pNIOBuffer->nioRef); + } + } + free(pAV->pANIOBuffers); + pAV->pANIOBuffers = NULL; + } + if(NULL != pAV->pAFrames) { + for(i=0; i<pAV->aFrameCount; i++) { + if(HAS_FUNC(sp_avcodec_free_frame)) { + sp_avcodec_free_frame(&pAV->pAFrames[i]); + } else { + sp_av_free(pAV->pAFrames[i]); + } + } + free(pAV->pAFrames); + pAV->pAFrames = NULL; + } + + // Close the video file + if(NULL != pAV->pFormatCtx) { + sp_avformat_close_input(&pAV->pFormatCtx); + // Only for output files! + // sp_avformat_free_context(pAV->pFormatCtx); + pAV->pFormatCtx = NULL; + } + if( NULL != pAV->ffmpegMediaPlayer ) { + (*env)->DeleteGlobalRef(env, pAV->ffmpegMediaPlayer); + pAV->ffmpegMediaPlayer = NULL; + } + + free(pAV); + } +} + +static int my_getPlaneCount(AVPixFmtDescriptor *pDesc) { + int i, p=-1; + for(i=pDesc->nb_components-1; i>=0; i--) { + int p0 = pDesc->comp[i].plane; + if( p < p0 ) { + p = p0; + } + } + return p+1; +} + +#if 0 +static int my_is_hwaccel_pix_fmt(enum PixelFormat pix_fmt) { + return sp_av_pix_fmt_descriptors[pix_fmt].flags & PIX_FMT_HWACCEL; +} + +static enum PixelFormat my_get_format(struct AVCodecContext *s, const enum PixelFormat * fmt) { + int i=0; + enum PixelFormat f0, fR = PIX_FMT_NONE; + char buf[256]; + + fprintf(stderr, "get_format ****\n"); + while (fmt[i] != PIX_FMT_NONE /* && ff_is_hwaccel_pix_fmt(fmt[i]) */) { + f0 = fmt[i]; + if(fR==PIX_FMT_NONE && !my_is_hwaccel_pix_fmt(f0)) { + fR = f0; + } + sp_av_get_pix_fmt_string(buf, sizeof(buf), f0); + fprintf(stderr, "get_format %d: %d - %s - %s\n", i, f0, sp_av_get_pix_fmt_name(f0), buf); + ++i; + } + fprintf(stderr, "get_format %d - %s *** \n", fR, sp_av_get_pix_fmt_name(fR)); + fflush(NULL); + return fR; +} +#endif + +JNIEXPORT jint JNICALL FF_FUNC(getAvUtilMajorVersionCC0) + (JNIEnv *env, jobject instance) { + return (jint) LIBAVUTIL_VERSION_MAJOR; +} + +JNIEXPORT jint JNICALL FF_FUNC(getAvFormatMajorVersionCC0) + (JNIEnv *env, jobject instance) { + return (jint) LIBAVFORMAT_VERSION_MAJOR; +} + +JNIEXPORT jint JNICALL FF_FUNC(getAvCodecMajorVersionCC0) + (JNIEnv *env, jobject instance) { + return (jint) LIBAVCODEC_VERSION_MAJOR; +} + +JNIEXPORT jint JNICALL FF_FUNC(getAvResampleMajorVersionCC0) + (JNIEnv *env, jobject instance) { + return (jint) LIBAVRESAMPLE_VERSION_MAJOR; +} + +JNIEXPORT jint JNICALL FF_FUNC(getSwResampleMajorVersionCC0) + (JNIEnv *env, jobject instance) { + return (jint) LIBSWRESAMPLE_VERSION_MAJOR; +} + +JNIEXPORT jlong JNICALL FF_FUNC(createInstance0) + (JNIEnv *env, jobject instance, jobject ffmpegMediaPlayer, jboolean verbose) +{ + FFMPEGToolBasicAV_t * pAV = calloc(1, sizeof(FFMPEGToolBasicAV_t)); + if(NULL==pAV) { + JoglCommon_throwNewRuntimeException(env, "Couldn't alloc instance"); + return 0; + } + 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(HAS_FUNC(sp_swresample_version)) { + pAV->swresampleVersion = sp_swresample_version(); + } else { + pAV->swresampleVersion = 0; + } + + #if LIBAVCODEC_VERSION_MAJOR >= 55 + // TODO: We keep code on using 1 a/v frame per decoding cycle now. + // This is compatible w/ OpenAL's alBufferData(..) + // and w/ OpenGL's texture update command, both copy data immediatly. + // pAV->useRefCountedFrames = AV_HAS_API_REFCOUNTED_FRAMES(pAV); + pAV->useRefCountedFrames = 0; + #else + pAV->useRefCountedFrames = 0; + #endif + + pAV->ffmpegMediaPlayer = (*env)->NewGlobalRef(env, ffmpegMediaPlayer); + pAV->verbose = verbose; + pAV->vid=AV_STREAM_ID_AUTO; + pAV->aid=AV_STREAM_ID_AUTO; + + if(pAV->verbose) { + fprintf(stderr, "Info: Use avresample %d, swresample %d, device %d, refCount %d\n", + AV_HAS_API_AVRESAMPLE(pAV), AV_HAS_API_SWRESAMPLE(pAV), HAS_FUNC(sp_avdevice_register_all), pAV->useRefCountedFrames); + } + return (jlong) (intptr_t) pAV; +} + +JNIEXPORT void JNICALL FF_FUNC(destroyInstance0) + (JNIEnv *env, jobject instance, jlong ptr) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + if (pAV != NULL) { + // stop assumed .. + freeInstance(env, pAV); + } +} + +static uint64_t getDefaultAudioChannelLayout(int channelCount) { + switch(channelCount) { + case 1: return AV_CH_LAYOUT_MONO; + case 2: return AV_CH_LAYOUT_STEREO; + case 3: return AV_CH_LAYOUT_SURROUND; + case 4: return AV_CH_LAYOUT_QUAD; + case 5: return AV_CH_LAYOUT_5POINT0; + case 6: return AV_CH_LAYOUT_5POINT1; + case 7: return AV_CH_LAYOUT_6POINT1; + case 8: return AV_CH_LAYOUT_7POINT1; + default: return AV_CH_LAYOUT_NATIVE; + } +} + +static void initPTSStats(PTSStats *ptsStats); +static int64_t evalPTS(PTSStats *ptsStats, int64_t inPTS, int64_t inDTS); + +static AVInputFormat* tryAVInputFormat(const char * name, int verbose) { + AVInputFormat* inFmt = sp_av_find_input_format(name); + if( verbose) { + if ( NULL == inFmt ) { + fprintf(stderr, "Warning: Could not find input format '%s'\n", name); + } else { + fprintf(stderr, "Info: Found input format '%s'\n", name); + } + } + return inFmt; +} +static const char * inFmtNames[] = { + "video4linux2", // linux + "video4linux", // linux (old) + "dshow", // windows + "vfwcap", // windows (old) + "mpg", + "yuv2", + "mjpeg", + "avi", + "wmv", + "libx264", + "h264", + "mpegts" +}; +static AVInputFormat* findAVInputFormat(int verbose) { + AVInputFormat* inFmt = NULL; + const char *inFmtName; + int i=0; + do { + inFmtName = inFmtNames[i++]; + if( NULL == inFmtName ) { + break; + } + inFmt = tryAVInputFormat(inFmtName, verbose); + } while ( NULL == inFmt ); + return inFmt; +} + +#if 0 +static void getAlignedLinesizes(AVCodecContext *avctx, int linesize[/*4*/]) { + int stride_align[AV_NUM_DATA_POINTERS]; + int w = avctx->width; + int h = avctx->height; + int unaligned; + int i; + + sp_avcodec_align_dimensions2(avctx, &w, &h, stride_align); + + if (!(avctx->flags & CODEC_FLAG_EMU_EDGE)) { + int edge_width = sp_avcodec_get_edge_width(); + w += edge_width * 2; + h += edge_width * 2; + } + + do { + // Get alignment for all planes (-> YUVP .. etc) + sp_av_image_fill_linesizes(linesize, avctx->pix_fmt, w); + // increase alignment of w for next try (rhs gives the lowest bit set in w) + w += w & ~(w - 1); + + unaligned = 0; + for (i = 0; i < 4; i++) + unaligned |= linesize[i] % stride_align[i]; + } while (unaligned); +} +#endif + +JNIEXPORT void JNICALL FF_FUNC(setStream0) + (JNIEnv *env, jobject instance, jlong ptr, jstring jURL, jboolean jIsCameraInput, + jint vid, jstring jSizeS, jint vWidth, jint vHeight, jint vRate, + jint aid, jint aMaxChannelCount, jint aPrefSampleRate) +{ + char cameraName[256]; + int res, i; + jboolean iscopy; + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)(intptr_t)ptr; + + if (pAV == NULL) { + JoglCommon_throwNewRuntimeException(env, "NULL AV ptr"); + return; + } + + // Register all formats and codecs + sp_avcodec_register_all(); + if( jIsCameraInput && HAS_FUNC(sp_avdevice_register_all) ) { + sp_avdevice_register_all(); + } + sp_av_register_all(); + // Network too .. + if(HAS_FUNC(sp_avformat_network_init)) { + sp_avformat_network_init(); + } + + pAV->pFormatCtx = sp_avformat_alloc_context(); + + const char *urlPath = (*env)->GetStringUTFChars(env, jURL, &iscopy); + const char *filename = urlPath; // allow changing path for camera .. + + // Open video file + AVDictionary *inOpts = NULL; + AVInputFormat* inFmt = NULL; + if( jIsCameraInput ) { + char buffer[256]; + inFmt = findAVInputFormat(pAV->verbose); + if( NULL == inFmt ) { + JoglCommon_throwNewRuntimeException(env, "Couldn't find input format for camera: %s", urlPath); + (*env)->ReleaseStringChars(env, jURL, (const jchar *)urlPath); + return; + } + if(pAV->verbose) { + fprintf(stderr, "Camera: Format: %s (%s)\n", inFmt->long_name, inFmt->name); + } + if( 0 == strncmp(inFmt->name, "dshow", 255) ) { + int devIdx = atoi(urlPath); + strncpy(cameraName, "video=", sizeof(cameraName)); + res = findDShowVideoDevice(cameraName+6, sizeof(cameraName)-6, devIdx, pAV->verbose); + if( 0 == res ) { + if(pAV->verbose) { + fprintf(stderr, "Camera %d found: %s\n", devIdx, cameraName); + } + filename = cameraName; + } else if(pAV->verbose) { + fprintf(stderr, "Camera %d not found\n", devIdx); + } + } + + const char *sizeS = NULL != jSizeS ? (*env)->GetStringUTFChars(env, jSizeS, &iscopy) : NULL; + int hasSize = 0; + if( NULL != sizeS ) { + snprintf(buffer, sizeof(buffer), "%s", sizeS); + (*env)->ReleaseStringChars(env, jSizeS, (const jchar *)sizeS); + hasSize = 1; + } else if( vWidth > 0 && vHeight > 0 ) { + snprintf(buffer, sizeof(buffer), "%dx%d", vWidth, vHeight); + hasSize = 1; + } + if( hasSize ) { + if(pAV->verbose) { + fprintf(stderr, "Camera: Size: %s\n", buffer); + } + sp_av_dict_set(&inOpts, "video_size", buffer, 0); + } + if( vRate > 0 ) { + snprintf(buffer, sizeof(buffer), "%d", vRate); + if(pAV->verbose) { + fprintf(stderr, "Camera: FPS: %s\n", buffer); + } + sp_av_dict_set(&inOpts, "framerate", buffer, 0); + } + } + + MY_MUTEX_LOCK(env, mutex_avcodec_openclose); + { + res = sp_avformat_open_input(&pAV->pFormatCtx, filename, inFmt, NULL != inOpts ? &inOpts : NULL); + if( NULL != inOpts ) { + sp_av_dict_free(&inOpts); + } + if(res != 0) { + MY_MUTEX_UNLOCK(env, mutex_avcodec_openclose); + JoglCommon_throwNewRuntimeException(env, "Couldn't open URI: %s [%dx%d @ %d hz], err %d", filename, vWidth, vHeight, vRate, res); + (*env)->ReleaseStringChars(env, jURL, (const jchar *)urlPath); + return; + } + + // Retrieve detailed stream information + if(sp_avformat_find_stream_info(pAV->pFormatCtx, NULL)<0) { + MY_MUTEX_UNLOCK(env, mutex_avcodec_openclose); + (*env)->ReleaseStringChars(env, jURL, (const jchar *)urlPath); + JoglCommon_throwNewRuntimeException(env, "Couldn't find stream information"); + return; + } + } + MY_MUTEX_UNLOCK(env, mutex_avcodec_openclose); + + if(pAV->verbose) { + // Dump information about file onto standard error + sp_av_dump_format(pAV->pFormatCtx, 0, filename, JNI_FALSE); + } + (*env)->ReleaseStringChars(env, jURL, (const jchar *)urlPath); + + // FIXME: Libav Binary compatibility! JAU01 + if (pAV->pFormatCtx->duration != AV_NOPTS_VALUE) { + pAV->duration = pAV->pFormatCtx->duration / AV_TIME_BASE_MSEC; + } + if (pAV->pFormatCtx->start_time != AV_NOPTS_VALUE) { + pAV->start_time = pAV->pFormatCtx->start_time / AV_TIME_BASE_MSEC; + } + if (pAV->pFormatCtx->bit_rate) { + pAV->bps_stream = pAV->pFormatCtx->bit_rate; + } + + if(pAV->verbose) { + fprintf(stderr, "Streams: %d, req vid %d aid %d\n", pAV->pFormatCtx->nb_streams, vid, aid); + } + + // Find the first audio and video stream, or the one matching vid + // FIXME: Libav Binary compatibility! JAU01 + for(i=0; ( AV_STREAM_ID_AUTO==pAV->aid || AV_STREAM_ID_AUTO==pAV->vid ) && i<pAV->pFormatCtx->nb_streams; i++) { + AVStream *st = pAV->pFormatCtx->streams[i]; + if(pAV->verbose) { + fprintf(stderr, "Stream: %d: is-video %d, is-audio %d\n", i, (AVMEDIA_TYPE_VIDEO == st->codec->codec_type), AVMEDIA_TYPE_AUDIO == st->codec->codec_type); + } + if(AVMEDIA_TYPE_VIDEO == st->codec->codec_type) { + if(AV_STREAM_ID_AUTO==pAV->vid && (AV_STREAM_ID_AUTO==vid || vid == i) ) { + pAV->pVStream = st; + pAV->vid=i; + } + } else if(AVMEDIA_TYPE_AUDIO == st->codec->codec_type) { + if(AV_STREAM_ID_AUTO==pAV->aid && (AV_STREAM_ID_AUTO==aid || aid == i) ) { + pAV->pAStream = st; + pAV->aid=i; + } + } + } + if( AV_STREAM_ID_AUTO == pAV->aid ) { + pAV->aid = AV_STREAM_ID_NONE; + } + if( AV_STREAM_ID_AUTO == pAV->vid ) { + pAV->vid = AV_STREAM_ID_NONE; + } + + if( pAV->verbose ) { + fprintf(stderr, "Found vid %d, aid %d\n", pAV->vid, pAV->aid); + } + + if(0<=pAV->aid) { + AVFrame * pAFrame0 = sp_avcodec_alloc_frame(); + if( NULL == pAFrame0 ) { + JoglCommon_throwNewRuntimeException(env, "Couldn't alloc 1st audio frame\n"); + return; + } + + // Get a pointer to the codec context for the audio stream + // FIXME: Libav Binary compatibility! JAU01 + pAV->pACodecCtx=pAV->pAStream->codec; + + // FIXME: Libav Binary compatibility! JAU01 + if (pAV->pACodecCtx->bit_rate) { + pAV->bps_audio = pAV->pACodecCtx->bit_rate; + } + + // Customize .. + // pAV->pACodecCtx->thread_count=2; + // pAV->pACodecCtx->thread_type=FF_THREAD_FRAME|FF_THREAD_SLICE; // Decode more than one frame at once + pAV->pACodecCtx->thread_count=0; + pAV->pACodecCtx->thread_type=0; + pAV->pACodecCtx->workaround_bugs=FF_BUG_AUTODETECT; + pAV->pACodecCtx->skip_frame=AVDISCARD_DEFAULT; + + // 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; + if( 1 <= aMaxChannelCount && aMaxChannelCount <= 2 ) { + pAV->pACodecCtx->request_channel_layout=getDefaultAudioChannelLayout(aMaxChannelCount); + #if LIBAVCODEC_VERSION_MAJOR < 54 + /** Until 55.0.0, but stopped working w/ 54 already :( */ + pAV->pACodecCtx->request_channels=aMaxChannelCount; + #endif + } + pAV->pACodecCtx->skip_frame=AVDISCARD_DEFAULT; + + sp_avcodec_string(pAV->acodec, sizeof(pAV->acodec), pAV->pACodecCtx, 0); + + // Find the decoder for the audio stream + pAV->pACodec=sp_avcodec_find_decoder(pAV->pACodecCtx->codec_id); + if(pAV->pACodec==NULL) { + JoglCommon_throwNewRuntimeException(env, "Couldn't find audio codec %d, %s", pAV->pACodecCtx->codec_id, pAV->acodec); + return; + } + + // Open codec + MY_MUTEX_LOCK(env, mutex_avcodec_openclose); + { + #if LIBAVCODEC_VERSION_MAJOR >= 55 + pAV->pACodecCtx->refcounted_frames = pAV->useRefCountedFrames; + #endif + res = sp_avcodec_open2(pAV->pACodecCtx, pAV->pACodec, NULL); + } + MY_MUTEX_UNLOCK(env, mutex_avcodec_openclose); + if(res<0) { + JoglCommon_throwNewRuntimeException(env, "Couldn't open audio codec %d, %s", pAV->pACodecCtx->codec_id, pAV->acodec); + return; + } + if (!pAV->pACodecCtx->channel_layout) { + pAV->pACodecCtx->channel_layout = getDefaultAudioChannelLayout(pAV->pACodecCtx->channels); + } + if (!pAV->pACodecCtx->channel_layout) { + JoglCommon_throwNewRuntimeException(env, "Couldn't determine channel layout of %d channels\n", pAV->pACodecCtx->channels); + return; + } + pAV->aSampleRate = pAV->pACodecCtx->sample_rate; + pAV->aChannels = pAV->pACodecCtx->channels; + 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, pAV->ffmpegMediaPlayer, pAV->aSampleFmt, pAV->aSampleRate, pAV->aChannels); + if( pAV->verbose ) { + fprintf(stderr, "A channels %d [l %"PRId64"], sample_rate %d, frame_size %d, frame_number %d, [afps %f, cfps %f, sfps %f], nb_frames %"PRId64", [maxChan %d, prefRate %d, req_chan_layout %"PRId64", 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->avg_frame_rate), + my_av_q2f_r(pAV->pAStream->codec->time_base), + my_av_q2f_r(pAV->pAStream->time_base), + pAV->pAStream->nb_frames, + aMaxChannelCount, aPrefSampleRate, pAV->pACodecCtx->request_channel_layout, + #if LIBAVCODEC_VERSION_MAJOR < 54 + pAV->pACodecCtx->request_channels, + #else + 0, + #endif + pAV->aSinkSupport); + } + + // default + pAV->aSampleFmtOut = pAV->aSampleFmt; + pAV->aChannelsOut = pAV->aChannels; + pAV->aSampleRateOut = pAV->aSampleRate; + + if( ( AV_HAS_API_AVRESAMPLE(pAV) || AV_HAS_API_SWRESAMPLE(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, pAV->ffmpegMediaPlayer, aSampleFmtOut, aPrefSampleRate, pAV->pACodecCtx->channels) ) { + aChannelsOut = pAV->pACodecCtx->channels; + aSampleRateOut = aPrefSampleRate; + aSinkSupport = 1; + } else if( _isAudioFormatSupported(env, pAV->ffmpegMediaPlayer, aSampleFmtOut, aPrefSampleRate, minChannelCount) ) { + aChannelsOut = minChannelCount; + aSampleRateOut = aPrefSampleRate; + aSinkSupport = 1; + } + + if( aSinkSupport ) { + if( AV_HAS_API_AVRESAMPLE(pAV) ) { + pAV->avResampleCtx = sp_avresample_alloc_context(); + sp_av_opt_set_int(pAV->avResampleCtx, "in_channel_layout", pAV->pACodecCtx->channel_layout, 0); + sp_av_opt_set_int(pAV->avResampleCtx, "out_channel_layout", getDefaultAudioChannelLayout(aChannelsOut), 0); + sp_av_opt_set_int(pAV->avResampleCtx, "in_sample_rate", pAV->aSampleRate, 0); + sp_av_opt_set_int(pAV->avResampleCtx, "out_sample_rate", aSampleRateOut, 0); + sp_av_opt_set_int(pAV->avResampleCtx, "in_sample_fmt", pAV->aSampleFmt, 0); + sp_av_opt_set_int(pAV->avResampleCtx, "out_sample_fmt", aSampleFmtOut, 0); + + if ( sp_avresample_open(pAV->avResampleCtx) < 0 ) { + sp_avresample_free(&pAV->avResampleCtx); + pAV->avResampleCtx = NULL; + fprintf(stderr, "error initializing avresample ctx\n"); + } else { + // OK + pAV->aSampleFmtOut = aSampleFmtOut; + pAV->aChannelsOut = aChannelsOut; + pAV->aSampleRateOut = aSampleRateOut; + pAV->aSinkSupport = 1; + } + } else if( AV_HAS_API_SWRESAMPLE(pAV) ) { + pAV->swResampleCtx = sp_swr_alloc(); + sp_av_opt_set_int(pAV->swResampleCtx, "in_channel_layout", pAV->pACodecCtx->channel_layout, 0); + sp_av_opt_set_int(pAV->swResampleCtx, "out_channel_layout", getDefaultAudioChannelLayout(aChannelsOut), 0); + sp_av_opt_set_int(pAV->swResampleCtx, "in_sample_rate", pAV->aSampleRate, 0); + sp_av_opt_set_int(pAV->swResampleCtx, "out_sample_rate", aSampleRateOut, 0); + sp_av_opt_set_sample_fmt(pAV->swResampleCtx, "in_sample_fmt", pAV->aSampleFmt, 0); + sp_av_opt_set_sample_fmt(pAV->swResampleCtx, "out_sample_fmt", aSampleFmtOut, 0); + + if ( sp_swr_init(pAV->swResampleCtx) < 0 ) { + sp_swr_free(&pAV->swResampleCtx); + pAV->swResampleCtx = NULL; + fprintf(stderr, "error initializing swresample ctx\n"); + } else { + // OK + pAV->aSampleFmtOut = aSampleFmtOut; + pAV->aChannelsOut = aChannelsOut; + pAV->aSampleRateOut = aSampleRateOut; + pAV->aSinkSupport = 1; + } + } + } + } + + // Allocate audio frames + // FIXME: Libav Binary compatibility! JAU01 + pAV->aFrameCount = 1; + pAV->pANIOBuffers = calloc(pAV->aFrameCount, sizeof(NIOBuffer_t)); + pAV->pAFrames = calloc(pAV->aFrameCount, sizeof(AVFrame*)); + pAV->pAFrames[0] = pAFrame0; + for(i=1; i<pAV->aFrameCount; i++) { + pAV->pAFrames[i] = sp_avcodec_alloc_frame(); + if( NULL == pAV->pAFrames[i] ) { + JoglCommon_throwNewRuntimeException(env, "Couldn't alloc audio frame %d / %d", i, pAV->aFrameCount); + return; + } + } + pAV->aFrameCurrent = 0; + } + + if(0<=pAV->vid) { + // Get a pointer to the codec context for the video stream + // FIXME: Libav Binary compatibility! JAU01 + pAV->pVCodecCtx=pAV->pVStream->codec; + #if 0 + pAV->pVCodecCtx->get_format = my_get_format; + #endif + + if (pAV->pVCodecCtx->bit_rate) { + // FIXME: Libav Binary compatibility! JAU01 + pAV->bps_video = pAV->pVCodecCtx->bit_rate; + } + + // Customize .. + // pAV->pVCodecCtx->thread_count=2; + // pAV->pVCodecCtx->thread_type=FF_THREAD_FRAME|FF_THREAD_SLICE; // Decode more than one frame at once + pAV->pVCodecCtx->thread_count=0; + pAV->pVCodecCtx->thread_type=0; + pAV->pVCodecCtx->workaround_bugs=FF_BUG_AUTODETECT; + pAV->pVCodecCtx->skip_frame=AVDISCARD_DEFAULT; + + sp_avcodec_string(pAV->vcodec, sizeof(pAV->vcodec), pAV->pVCodecCtx, 0); + + // Find the decoder for the video stream + pAV->pVCodec=sp_avcodec_find_decoder(pAV->pVCodecCtx->codec_id); + if(pAV->pVCodec==NULL) { + JoglCommon_throwNewRuntimeException(env, "Couldn't find video codec %d, %s", pAV->pVCodecCtx->codec_id, pAV->vcodec); + return; + } + + // Open codec + MY_MUTEX_LOCK(env, mutex_avcodec_openclose); + { + #if LIBAVCODEC_VERSION_MAJOR >= 55 + pAV->pVCodecCtx->refcounted_frames = pAV->useRefCountedFrames; + #endif + res = sp_avcodec_open2(pAV->pVCodecCtx, pAV->pVCodec, NULL); + } + MY_MUTEX_UNLOCK(env, mutex_avcodec_openclose); + if(res<0) { + JoglCommon_throwNewRuntimeException(env, "Couldn't open video codec %d, %s", pAV->pVCodecCtx->codec_id, pAV->vcodec); + return; + } + + // Hack to correct wrong frame rates that seem to be generated by some codecs + // FIXME: Libav Binary compatibility! JAU01 + if(pAV->pVCodecCtx->time_base.num>1000 && pAV->pVCodecCtx->time_base.den==1) { + pAV->pVCodecCtx->time_base.den=1000; + } + // FIXME: Libav Binary compatibility! JAU01 + if( pAV->pVStream->avg_frame_rate.den && pAV->pVStream->avg_frame_rate.num ) { + pAV->fps = my_av_q2f(pAV->pVStream->avg_frame_rate); + #if LIBAVCODEC_VERSION_MAJOR < 55 + } else if( pAV->pVStream->r_frame_rate.den && pAV->pVStream->r_frame_rate.num ) { + pAV->fps = my_av_q2f(pAV->pVStream->r_frame_rate); + #endif + } else if( pAV->pVStream->codec->time_base.den && pAV->pVStream->codec->time_base.num ) { + pAV->fps = my_av_q2f_r(pAV->pVStream->codec->time_base); + } else if( pAV->pVStream->time_base.den && pAV->pVStream->time_base.num ) { + pAV->fps = my_av_q2f_r(pAV->pVStream->time_base); + } else { + pAV->fps = 0.0f; // duh! + } + pAV->frames_video = pAV->pVStream->nb_frames; + + // Allocate video frame + // FIXME: Libav Binary compatibility! JAU01 + pAV->vWidth = pAV->pVCodecCtx->width; + pAV->vHeight = pAV->pVCodecCtx->height; + pAV->vPixFmt = pAV->pVCodecCtx->pix_fmt; + pAV->vFlipped = JNI_FALSE; + { + AVPixFmtDescriptor pixDesc = sp_av_pix_fmt_descriptors[pAV->vPixFmt]; + pAV->vBitsPerPixel = sp_av_get_bits_per_pixel(&pixDesc); + pAV->vBufferPlanes = my_getPlaneCount(&pixDesc); + } + + if( pAV->verbose ) { + fprintf(stderr, "V frame_size %d, frame_number %d, [afps %f, rfps %f, cfps %f, sfps %f] -> %f fps, nb_frames %"PRId64", size %dx%d, fmt 0x%X, bpp %d, planes %d, codecCaps 0x%X\n", + pAV->pVCodecCtx->frame_size, pAV->pVCodecCtx->frame_number, + my_av_q2f(pAV->pVStream->avg_frame_rate), + #if LIBAVCODEC_VERSION_MAJOR < 55 + my_av_q2f(pAV->pVStream->r_frame_rate), + #else + 0.0f, + #endif + my_av_q2f_r(pAV->pVStream->codec->time_base), + my_av_q2f_r(pAV->pVStream->time_base), + pAV->fps, + pAV->pVStream->nb_frames, + pAV->vWidth, pAV->vHeight, pAV->vPixFmt, pAV->vBitsPerPixel, pAV->vBufferPlanes, pAV->pVCodecCtx->codec->capabilities); + } + + pAV->pVFrame=sp_avcodec_alloc_frame(); + if( pAV->pVFrame == NULL ) { + JoglCommon_throwNewRuntimeException(env, "Couldn't alloc video frame"); + return; + } + // Min. requirement for 'get_buffer2' ! + pAV->pVFrame->width = pAV->pVCodecCtx->width; + pAV->pVFrame->height = pAV->pVCodecCtx->height; + pAV->pVFrame->format = pAV->pVCodecCtx->pix_fmt; + #if LIBAVCODEC_VERSION_MAJOR >= 55 + res = sp_avcodec_default_get_buffer2(pAV->pVCodecCtx, pAV->pVFrame, 0); + #else + res = sp_avcodec_default_get_buffer(pAV->pVCodecCtx, pAV->pVFrame); + #endif + if(0!=res) { + JoglCommon_throwNewRuntimeException(env, "Couldn't peek video buffer dimension"); + return; + } + { + const int32_t bytesPerPixel = ( pAV->vBitsPerPixel + 7 ) / 8 ; + if(1 == pAV->vBufferPlanes) { + pAV->vBytesPerPixelPerPlane = bytesPerPixel; + } else { + pAV->vBytesPerPixelPerPlane = 1; + } + int32_t vLinesize[4]; + if( pAV->vBufferPlanes > 1 ) { + #if 0 + getAlignedLinesizes(pAV->pVCodecCtx, vLinesize); + for(i=0; i<pAV->vBufferPlanes; i++) { + // FIXME: Libav Binary compatibility! JAU01 + pAV->vTexWidth[i] = vLinesize[i] / pAV->vBytesPerPixelPerPlane ; + } + #else + for(i=0; i<pAV->vBufferPlanes; i++) { + // FIXME: Libav Binary compatibility! JAU01 + vLinesize[i] = pAV->pVFrame->linesize[i]; + pAV->vTexWidth[i] = vLinesize[i] / pAV->vBytesPerPixelPerPlane ; + } + #endif + } else { + vLinesize[0] = pAV->pVFrame->linesize[0]; + if( pAV->vPixFmt == PIX_FMT_YUYV422 ) { + // Stuff 2x 16bpp (YUYV) into one RGBA pixel! + pAV->vTexWidth[0] = pAV->pVCodecCtx->width / 2; + } else { + pAV->vTexWidth[0] = pAV->pVCodecCtx->width; + } + } + if( pAV->verbose ) { + for(i=0; i<pAV->vBufferPlanes; i++) { + fprintf(stderr, "Video: P[%d]: %d texw * %d bytesPP -> %d line\n", i, pAV->vTexWidth[i], pAV->vBytesPerPixelPerPlane, vLinesize[i]); + } + } + } + #if LIBAVCODEC_VERSION_MAJOR >= 55 + sp_av_frame_unref(pAV->pVFrame); + #else + sp_avcodec_default_release_buffer(pAV->pVCodecCtx, pAV->pVFrame); + #endif + } + pAV->vPTS=0; + pAV->aPTS=0; + initPTSStats(&pAV->vPTSStats); + initPTSStats(&pAV->aPTSStats); + _updateJavaAttributes(env, pAV); +} + +JNIEXPORT void JNICALL FF_FUNC(setGLFuncs0) + (JNIEnv *env, jobject instance, jlong ptr, jlong jProcAddrGLTexSubImage2D, jlong jProcAddrGLGetError, jlong jProcAddrGLFlush, jlong jProcAddrGLFinish) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + pAV->procAddrGLTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) (intptr_t)jProcAddrGLTexSubImage2D; + pAV->procAddrGLGetError = (PFNGLGETERRORPROC) (intptr_t)jProcAddrGLGetError; + pAV->procAddrGLFlush = (PFNGLFLUSH) (intptr_t)jProcAddrGLFlush; + pAV->procAddrGLFinish = (PFNGLFINISH) (intptr_t)jProcAddrGLFinish; +} + +#if 0 +#define DBG_TEXSUBIMG2D_a(c,p,w1,w2,h,i) fprintf(stderr, "TexSubImage2D.%c offset %d / %d, size %d x %d, ", c, (w1*p->pVCodecCtx->width)/w2, p->pVCodecCtx->height/h, p->vTexWidth[i], p->pVCodecCtx->height/h) +#define DBG_TEXSUBIMG2D_b(p) fprintf(stderr, "err 0x%X\n", pAV->procAddrGLGetError()) +#else +#define DBG_TEXSUBIMG2D_a(c,p,w1,w2,h,i) +#define DBG_TEXSUBIMG2D_b(p) +#endif + +JNIEXPORT jint JNICALL FF_FUNC(readNextPacket0) + (JNIEnv *env, jobject instance, jlong ptr, jint texTarget, jint texFmt, jint texType) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + + AVPacket packet; + jint resPTS = INVALID_PTS; + uint8_t * pkt_odata; + int pkt_osize; + + packet.data = NULL; // minimum + packet.size = 0; // requirement + sp_av_init_packet(&packet); + + const int avRes = sp_av_read_frame(pAV->pFormatCtx, &packet); + pkt_odata = packet.data; + pkt_osize = packet.size; + if( AVERROR_EOF == avRes || ( pAV->pFormatCtx->pb && pAV->pFormatCtx->pb->eof_reached ) ) { + if( pAV->verbose ) { + fprintf(stderr, "EOS: avRes[res %d, eos %d], pb-EOS %d\n", + avRes, AVERROR_EOF == avRes, + ( pAV->pFormatCtx->pb && pAV->pFormatCtx->pb->eof_reached ) ); + } + resPTS = END_OF_STREAM_PTS; + } else if( 0 <= avRes ) { + if( pAV->verbose ) { + fprintf(stderr, "P: ptr %p, size %d\n", packet.data, packet.size); + } + if(packet.stream_index==pAV->aid) { + // Decode audio frame + if(NULL == pAV->pAFrames) { // no audio registered + sp_av_free_packet(&packet); + return INVALID_PTS; + } + int frameCount; + int flush_complete = 0; + for ( frameCount=0; 0 < packet.size || 0 == frameCount; frameCount++ ) { + int frameDecoded; + int len1; + AVFrame* pAFrameCurrent = pAV->pAFrames[pAV->aFrameCurrent]; + if( pAV->useRefCountedFrames ) { + sp_av_frame_unref(pAFrameCurrent); + pAV->aFrameCurrent = ( pAV->aFrameCurrent + 1 ) % pAV->aFrameCount ; + } + sp_avcodec_get_frame_defaults(pAFrameCurrent); + + if (flush_complete) { + break; + } + len1 = sp_avcodec_decode_audio4(pAV->pACodecCtx, pAFrameCurrent, &frameDecoded, &packet); + if (len1 < 0) { + // if error, we skip the frame + packet.size = 0; + break; + } + packet.data += len1; + packet.size -= len1; + + if (!frameDecoded) { + // stop sending empty packets if the decoder is finished + if (!packet.data && pAV->pACodecCtx->codec->capabilities & CODEC_CAP_DELAY) { + flush_complete = 1; + } + continue; + } + + int32_t data_size = 0; + if(HAS_FUNC(sp_av_samples_get_buffer_size)) { + data_size = sp_av_samples_get_buffer_size(NULL /* linesize, may be NULL */, + pAV->aChannels, + pAFrameCurrent->nb_samples, + pAFrameCurrent->format, + 1 /* align */); + } + #if 0 + fprintf(stderr, "channels %d sample_rate %d \n", pAV->aChannels , pAV->aSampleRate); + fprintf(stderr, "data %d \n", pAV->aFrameSize); + #endif + + const AVRational time_base = pAV->pAStream->time_base; + const int64_t pkt_pts = pAFrameCurrent->pkt_pts; + 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 = sp_av_get_bytes_per_sample( pAV->pACodecCtx->sample_fmt ); + pAV->aPTS += data_size / ( pAV->aChannels * bytesPerSample * ( pAV->aSampleRate / 1000 ) ); + } + if( pAV->verbose ) { + int32_t aDTS = my_av_q2i32( pAFrameCurrent->pkt_dts * 1000, time_base); + + fprintf(stderr, "A pts %d [pkt_pts %"PRId64"], dts %d [pkt_dts %"PRId64"], f# %d, aFrame %d/%d %p, dataPtr %p, dataSize %d\n", + pAV->aPTS, pkt_pts, aDTS, pAFrameCurrent->pkt_dts, frameCount, + pAV->aFrameCurrent, pAV->aFrameCount, pAFrameCurrent, pAFrameCurrent->data[0], data_size); + } + if( NULL != env ) { + void* data_ptr = pAFrameCurrent->data[0]; // default + + if( NULL != pAV->avResampleCtx || NULL != pAV->swResampleCtx ) { + uint8_t *tmp_out; + int out_samples=-1, 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 = sp_av_realloc(pAV->aResampleBuffer, out_size); + if (!tmp_out) { + JoglCommon_throwNewRuntimeException(env, "Couldn't alloc resample buffer of size %d", out_size); + return INVALID_PTS; + } + pAV->aResampleBuffer = tmp_out; + + if( NULL != pAV->avResampleCtx ) { + out_samples = sp_avresample_convert(pAV->avResampleCtx, + &pAV->aResampleBuffer, + out_linesize, nb_samples, + pAFrameCurrent->data, + pAFrameCurrent->linesize[0], + pAFrameCurrent->nb_samples); + } else if( NULL != pAV->swResampleCtx ) { + out_samples = sp_swr_convert(pAV->swResampleCtx, + &pAV->aResampleBuffer, nb_samples, + (const uint8_t **)pAFrameCurrent->data, pAFrameCurrent->nb_samples); + } + if (out_samples < 0) { + JoglCommon_throwNewRuntimeException(env, "avresample_convert() failed"); + return INVALID_PTS; + } + 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 && ( 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); + } + (*env)->DeleteGlobalRef(env, pNIOBufferCurrent->nioRef); + newNIO = 1; + } + if( newNIO ) { + jobject jSampleData = (*env)->NewDirectByteBuffer(env, data_ptr, data_size); + pNIOBufferCurrent->nioRef = (*env)->NewGlobalRef(env, jSampleData); + pNIOBufferCurrent->origPtr = data_ptr; + pNIOBufferCurrent->size = data_size; + (*env)->DeleteLocalRef(env, jSampleData); + if(pAV->verbose) { + fprintf(stderr, "A NIO: Alloc ptr %p / ref %p, %d bytes\n", + pNIOBufferCurrent->origPtr, pNIOBufferCurrent->nioRef, pNIOBufferCurrent->size); + } + } + (*env)->CallVoidMethod(env, pAV->ffmpegMediaPlayer, ffmpeg_jni_mid_pushSound, pNIOBufferCurrent->nioRef, data_size, pAV->aPTS); + } + } + } else if(packet.stream_index==pAV->vid) { + // Decode video frame + if(NULL == pAV->pVFrame) { + sp_av_free_packet(&packet); + return INVALID_PTS; + } + int frameCount; + int flush_complete = 0; + for ( frameCount=0; 0 < packet.size || 0 == frameCount; frameCount++ ) { + int frameDecoded; + int len1; + sp_avcodec_get_frame_defaults(pAV->pVFrame); + if (flush_complete) { + break; + } + len1 = sp_avcodec_decode_video2(pAV->pVCodecCtx, pAV->pVFrame, &frameDecoded, &packet); + if (len1 < 0) { + // if error, we skip the frame + packet.size = 0; + break; + } + packet.data += len1; + packet.size -= len1; + + if (!frameDecoded) { + // stop sending empty packets if the decoder is finished + if (!packet.data && pAV->pVCodecCtx->codec->capabilities & CODEC_CAP_DELAY) { + flush_complete = 1; + } + continue; + } + + // FIXME: Libav Binary compatibility! JAU01 + const AVRational time_base = pAV->pVStream->time_base; + const int64_t pkt_pts = pAV->pVFrame->pkt_pts; + const int64_t pkt_dts = pAV->pVFrame->pkt_dts; + const int64_t fix_pts = evalPTS(&pAV->vPTSStats, pkt_pts, pkt_dts); + if( AV_NOPTS_VALUE != fix_pts ) { // discard invalid PTS .. + pAV->vPTS = my_av_q2i32( fix_pts * 1000, time_base); + } + if( pAV->verbose ) { + const int32_t vPTS = AV_NOPTS_VALUE != pkt_pts ? my_av_q2i32( pkt_pts * 1000, time_base) : 0; + const int32_t vDTS = AV_NOPTS_VALUE != pkt_dts ? my_av_q2i32( pkt_dts * 1000, time_base) : 0; + + const double frame_delay_d = av_q2d(pAV->pVCodecCtx->time_base); + const double frame_repeat_d = pAV->pVFrame->repeat_pict * (frame_delay_d * 0.5); + + const int32_t frame_delay_i = my_av_q2i32(1000, pAV->pVCodecCtx->time_base); + const int32_t frame_repeat_i = pAV->pVFrame->repeat_pict * (frame_delay_i / 2); + + const char * warn = frame_repeat_i > 0 ? "REPEAT" : "NORMAL" ; + + fprintf(stderr, "V fix_pts %d, pts %d [pkt_pts %"PRId64"], dts %d [pkt_dts %"PRId64"], time d(%lf s + r %lf = %lf s), i(%d ms + r %d = %d ms) - %s - f# %d, dec %d, data %p, lsz %d\n", + pAV->vPTS, vPTS, pkt_pts, vDTS, pkt_dts, + frame_delay_d, frame_repeat_d, (frame_delay_d + frame_repeat_d), + frame_delay_i, frame_repeat_i, (frame_delay_i + frame_repeat_i), warn, frameCount, + len1, pAV->pVFrame->data[0], pAV->pVFrame->linesize[0]); + // fflush(NULL); + } + if( 0 == pAV->pVFrame->linesize[0] ) { + if( pAV->useRefCountedFrames ) { + sp_av_frame_unref(pAV->pVFrame); + } + continue; + } + resPTS = pAV->vPTS; // Video Frame! + + int p_offset[] = { 0, 0, 0, 0 }; + if( pAV->pVFrame->linesize[0] < 0 ) { + if( JNI_FALSE == pAV->vFlipped ) { + pAV->vFlipped = JNI_TRUE; + _setIsGLOriented(env, pAV); + } + + // image bottom-up + int h_1 = pAV->pVCodecCtx->height - 1; + p_offset[0] = pAV->pVFrame->linesize[0] * h_1; + if( pAV->vBufferPlanes > 1 ) { + p_offset[1] = pAV->pVFrame->linesize[1] * h_1; + } + if( pAV->vBufferPlanes > 2 ) { + p_offset[2] = pAV->pVFrame->linesize[2] * h_1; + } + /** + if( pAV->vBufferPlanes > 3 ) { + p_offset[3] = pAV->pVFrame->linesize[3] * h_1; + } */ + } else if( JNI_TRUE == pAV->vFlipped ) { + pAV->vFlipped = JNI_FALSE; + _setIsGLOriented(env, pAV); + } + + // 1st plane or complete packed frame + // FIXME: Libav Binary compatibility! JAU01 + DBG_TEXSUBIMG2D_a('Y',pAV,1,1,1,0); + pAV->procAddrGLTexSubImage2D(texTarget, 0, + 0, 0, + pAV->vTexWidth[0], pAV->pVCodecCtx->height, + texFmt, texType, pAV->pVFrame->data[0] + p_offset[0]); + DBG_TEXSUBIMG2D_b(pAV); + + if( pAV->vPixFmt == PIX_FMT_YUV420P || pAV->vPixFmt == PIX_FMT_YUVJ420P ) { + // U plane + // FIXME: Libav Binary compatibility! JAU01 + DBG_TEXSUBIMG2D_a('U',pAV,1,1,2,1); + pAV->procAddrGLTexSubImage2D(texTarget, 0, + pAV->pVCodecCtx->width, 0, + pAV->vTexWidth[1], pAV->pVCodecCtx->height/2, + texFmt, texType, pAV->pVFrame->data[1] + p_offset[1]); + DBG_TEXSUBIMG2D_b(pAV); + // V plane + // FIXME: Libav Binary compatibility! JAU01 + DBG_TEXSUBIMG2D_a('V',pAV,1,1,2,2); + pAV->procAddrGLTexSubImage2D(texTarget, 0, + pAV->pVCodecCtx->width, pAV->pVCodecCtx->height/2, + pAV->vTexWidth[2], pAV->pVCodecCtx->height/2, + texFmt, texType, pAV->pVFrame->data[2] + p_offset[2]); + DBG_TEXSUBIMG2D_b(pAV); + } else if( pAV->vPixFmt == PIX_FMT_YUV422P || pAV->vPixFmt == PIX_FMT_YUVJ422P ) { + // U plane + // FIXME: Libav Binary compatibility! JAU01 + DBG_TEXSUBIMG2D_a('U',pAV,1,1,1,1); + pAV->procAddrGLTexSubImage2D(texTarget, 0, + pAV->pVCodecCtx->width, 0, + pAV->vTexWidth[1], pAV->pVCodecCtx->height, + texFmt, texType, pAV->pVFrame->data[1] + p_offset[1]); + DBG_TEXSUBIMG2D_b(pAV); + // V plane + // FIXME: Libav Binary compatibility! JAU01 + DBG_TEXSUBIMG2D_a('V',pAV,3,2,1,1); + pAV->procAddrGLTexSubImage2D(texTarget, 0, + pAV->pVCodecCtx->width+pAV->pVCodecCtx->width/2, 0, + pAV->vTexWidth[2], pAV->pVCodecCtx->height, + texFmt, texType, pAV->pVFrame->data[2] + p_offset[2]); + DBG_TEXSUBIMG2D_b(pAV); + } // FIXME: Add more planar formats ! + + pAV->procAddrGLFinish(); + //pAV->procAddrGLFlush(); + if( pAV->useRefCountedFrames ) { + sp_av_frame_unref(pAV->pVFrame); + } + } + } + // restore orig pointer and size values, we may have moved along within packet + packet.data = pkt_odata; + packet.size = pkt_osize; + sp_av_free_packet(&packet); + } + return resPTS; +} + +static void initPTSStats(PTSStats *ptsStats) { + ptsStats->ptsError = 0; + ptsStats->dtsError = 0; + ptsStats->ptsLast = INT64_MIN; + ptsStats->dtsLast = INT64_MIN; +} +static int64_t evalPTS(PTSStats *ptsStats, int64_t inPTS, int64_t inDTS) { + int64_t resPTS = AV_NOPTS_VALUE; + + if ( inDTS != AV_NOPTS_VALUE ) { + ptsStats->dtsError += inDTS <= ptsStats->dtsLast; + ptsStats->dtsLast = inDTS; + } + if ( inPTS != AV_NOPTS_VALUE ) { + ptsStats->ptsError += inPTS <= ptsStats->ptsLast; + ptsStats->ptsLast = inPTS; + } + if ( inPTS != AV_NOPTS_VALUE && + ( ptsStats->ptsError<=ptsStats->dtsError || inDTS == AV_NOPTS_VALUE ) ) { + resPTS = inPTS; + } else { + resPTS = inDTS; + } + return resPTS; +} + + +JNIEXPORT jint JNICALL FF_FUNC(play0) + (JNIEnv *env, jobject instance, jlong ptr) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + return sp_av_read_play(pAV->pFormatCtx); +} +JNIEXPORT jint JNICALL FF_FUNC(pause0) + (JNIEnv *env, jobject instance, jlong ptr) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + return sp_av_read_pause(pAV->pFormatCtx); +} + +JNIEXPORT jint JNICALL FF_FUNC(seek0) + (JNIEnv *env, jobject instance, jlong ptr, jint pos1) +{ + const FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + int64_t pos0, pts0; + int streamID; + AVRational time_base; + if( pAV->vid >= 0 ) { + pos0 = pAV->vPTS; + streamID = pAV->vid; + time_base = pAV->pVStream->time_base; + pts0 = pAV->pVFrame->pkt_pts; + } else if( pAV->aid >= 0 ) { + pos0 = pAV->aPTS; + streamID = pAV->aid; + time_base = pAV->pAStream->time_base; + pts0 = pAV->pAFrames[pAV->aFrameCurrent]->pkt_pts; + } else { + return pAV->vPTS; + } + int64_t pts1 = (int64_t) (pos1 * (int64_t) time_base.den) + / (1000 * (int64_t) time_base.num); + if(pAV->verbose) { + fprintf(stderr, "SEEK: vid %d, aid %d, pos0 %"PRId64", pos1 %d, pts: %"PRId64" -> %"PRId64"\n", pAV->vid, pAV->aid, pos0, pos1, pts0, pts1); + } + int flags = 0; + if(pos1 < pos0) { + flags |= AVSEEK_FLAG_BACKWARD; + } + int res = -2; + if(HAS_FUNC(sp_av_seek_frame)) { + if(pAV->verbose) { + fprintf(stderr, "SEEK.0: pre : s %"PRId64" / %"PRId64" -> t %d / %"PRId64"\n", pos0, pts0, pos1, pts1); + } + sp_av_seek_frame(pAV->pFormatCtx, streamID, pts1, flags); + } else if(HAS_FUNC(sp_avformat_seek_file)) { + int64_t ptsD = pts1 - pts0; + int64_t seek_min = ptsD > 0 ? pts1 - ptsD : INT64_MIN; + int64_t seek_max = ptsD < 0 ? pts1 - ptsD : INT64_MAX; + if(pAV->verbose) { + fprintf(stderr, "SEEK.1: pre : s %"PRId64" / %"PRId64" -> t %d / %"PRId64" [%"PRId64" .. %"PRId64"]\n", + pos0, pts0, pos1, pts1, seek_min, seek_max); + } + res = sp_avformat_seek_file(pAV->pFormatCtx, -1, seek_min, pts1, seek_max, flags); + } + if(NULL != pAV->pVCodecCtx) { + sp_avcodec_flush_buffers( pAV->pVCodecCtx ); + } + if(NULL != pAV->pACodecCtx) { + sp_avcodec_flush_buffers( pAV->pACodecCtx ); + } + const jint rPTS = my_av_q2i32( ( pAV->vid >= 0 ? pAV->pVFrame->pkt_pts : pAV->pAFrames[pAV->aFrameCurrent]->pkt_pts ) * 1000, time_base); + if(pAV->verbose) { + fprintf(stderr, "SEEK: post : res %d, u %d\n", res, rPTS); + } + return rPTS; +} + +JNIEXPORT jint JNICALL FF_FUNC(getVideoPTS0) + (JNIEnv *env, jobject instance, jlong ptr) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + return pAV->vPTS; +} + +JNIEXPORT jint JNICALL FF_FUNC(getAudioPTS0) + (JNIEnv *env, jobject instance, jlong ptr) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + return pAV->aPTS; +} + diff --git a/src/jogl/native/libav/ffmpeg_lavc53_lavf53_lavu51.c b/src/jogl/native/libav/ffmpeg_lavc53_lavf53_lavu51.c new file mode 100644 index 000000000..edce2ba1d --- /dev/null +++ b/src/jogl/native/libav/ffmpeg_lavc53_lavf53_lavu51.c @@ -0,0 +1,33 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +#include "jogamp_opengl_util_av_impl_FFMPEGv08Natives.h" + +#define FF_FUNC(METHOD) Java_jogamp_opengl_util_av_impl_FFMPEGv08Natives_ ## METHOD + +#include "ffmpeg_impl_template.c" diff --git a/src/jogl/native/libav/ffmpeg_lavc54_lavf54_lavu52_lavr01.c b/src/jogl/native/libav/ffmpeg_lavc54_lavf54_lavu52_lavr01.c new file mode 100644 index 000000000..651a64976 --- /dev/null +++ b/src/jogl/native/libav/ffmpeg_lavc54_lavf54_lavu52_lavr01.c @@ -0,0 +1,33 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +#include "jogamp_opengl_util_av_impl_FFMPEGv09Natives.h" + +#define FF_FUNC(METHOD) Java_jogamp_opengl_util_av_impl_FFMPEGv09Natives_ ## METHOD + +#include "ffmpeg_impl_template.c" diff --git a/src/jogl/native/libav/ffmpeg_lavc55_lavf55_lavu52_lavr01.c b/src/jogl/native/libav/ffmpeg_lavc55_lavf55_lavu52_lavr01.c new file mode 100644 index 000000000..277100398 --- /dev/null +++ b/src/jogl/native/libav/ffmpeg_lavc55_lavf55_lavu52_lavr01.c @@ -0,0 +1,33 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +#include "jogamp_opengl_util_av_impl_FFMPEGv10Natives.h" + +#define FF_FUNC(METHOD) Java_jogamp_opengl_util_av_impl_FFMPEGv10Natives_ ## METHOD + +#include "ffmpeg_impl_template.c" diff --git a/src/jogl/native/libav/ffmpeg_static.c b/src/jogl/native/libav/ffmpeg_static.c new file mode 100644 index 000000000..c8af59540 --- /dev/null +++ b/src/jogl/native/libav/ffmpeg_static.c @@ -0,0 +1,91 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +#include "ffmpeg_static.h" + +#include "JoglCommon.h" + +#include <GL/gl-platform.h> + +static const char * const ClazzNameFFMPEGMediaPlayer = "jogamp/opengl/util/av/impl/FFMPEGMediaPlayer"; + +static jclass ffmpegMediaPlayerClazz = NULL; +jmethodID ffmpeg_jni_mid_pushSound = NULL; +jmethodID ffmpeg_jni_mid_updateAttributes = NULL; +jmethodID ffmpeg_jni_mid_setIsGLOriented = NULL; +jmethodID ffmpeg_jni_mid_setupFFAttributes = NULL; +jmethodID ffmpeg_jni_mid_isAudioFormatSupported = NULL; + +typedef unsigned (APIENTRYP AV_GET_VERSION)(void); + +JNIEXPORT jboolean JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGStaticNatives_initIDs0 + (JNIEnv *env, jclass clazz) +{ + jboolean res = JNI_TRUE; + JoglCommon_init(env); + + jclass c; + if (ffmpegMediaPlayerClazz != NULL) { + return JNI_FALSE; + } + + c = (*env)->FindClass(env, ClazzNameFFMPEGMediaPlayer); + if(NULL==c) { + JoglCommon_FatalError(env, "JOGL FFMPEG: can't find %s", ClazzNameFFMPEGMediaPlayer); + } + ffmpegMediaPlayerClazz = (jclass)(*env)->NewGlobalRef(env, c); + (*env)->DeleteLocalRef(env, c); + if(NULL==ffmpegMediaPlayerClazz) { + JoglCommon_FatalError(env, "JOGL FFMPEG: can't use %s", ClazzNameFFMPEGMediaPlayer); + } + + ffmpeg_jni_mid_pushSound = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "pushSound", "(Ljava/nio/ByteBuffer;II)V"); + ffmpeg_jni_mid_updateAttributes = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "updateAttributes", "(IIIIIIIFIIILjava/lang/String;Ljava/lang/String;)V"); + ffmpeg_jni_mid_setIsGLOriented = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "setIsGLOriented", "(Z)V"); + ffmpeg_jni_mid_setupFFAttributes = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "setupFFAttributes", "(IIIIIIIIIIIIIII)V"); + ffmpeg_jni_mid_isAudioFormatSupported = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "isAudioFormatSupported", "(III)Z"); + + if(ffmpeg_jni_mid_pushSound == NULL || + ffmpeg_jni_mid_updateAttributes == NULL || + ffmpeg_jni_mid_setIsGLOriented == NULL || + ffmpeg_jni_mid_setupFFAttributes == NULL || + ffmpeg_jni_mid_isAudioFormatSupported == NULL) { + return JNI_FALSE; + } + return res; +} + +JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGStaticNatives_getAvVersion0 + (JNIEnv *env, jclass clazz, jlong func) { + if( 0 != func ) { + return (jint) ((AV_GET_VERSION) (intptr_t) func)(); + } else { + return 0; + } +} + diff --git a/src/jogl/native/libav/ffmpeg_static.h b/src/jogl/native/libav/ffmpeg_static.h new file mode 100644 index 000000000..ab4db2506 --- /dev/null +++ b/src/jogl/native/libav/ffmpeg_static.h @@ -0,0 +1,50 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +#ifndef _FFMPEG_STATIC_H +#define _FFMPEG_STATIC_H + +#ifdef _WIN32 + #include <windows.h> +#endif + +#include <gluegen_stdint.h> +#include <gluegen_inttypes.h> +#include <gluegen_stddef.h> +#include <gluegen_stdint.h> + +#include "jogamp_opengl_util_av_impl_FFMPEGStaticNatives.h" + +extern jmethodID ffmpeg_jni_mid_pushSound; +extern jmethodID ffmpeg_jni_mid_updateAttributes; +extern jmethodID ffmpeg_jni_mid_setIsGLOriented; +extern jmethodID ffmpeg_jni_mid_setupFFAttributes; +extern jmethodID ffmpeg_jni_mid_isAudioFormatSupported; + +#endif /* _FFMPEG_STATIC_H */ + diff --git a/src/jogl/native/libav/ffmpeg_tool.h b/src/jogl/native/libav/ffmpeg_tool.h index 3181a8a8f..136be2ecc 100644 --- a/src/jogl/native/libav/ffmpeg_tool.h +++ b/src/jogl/native/libav/ffmpeg_tool.h @@ -41,28 +41,112 @@ #include <gluegen_stddef.h> #include <gluegen_stdint.h> -#include <libavcodec/avcodec.h> -#include <libavformat/avformat.h> +#include "libavcodec/avcodec.h" +#include "libavformat/avformat.h" +#include "libavutil/avutil.h" +#if LIBAVCODEC_VERSION_MAJOR >= 54 + #include "libavresample/avresample.h" + #include "libswresample/swresample.h" +#endif + +#ifndef LIBAVRESAMPLE_VERSION_MAJOR +#define LIBAVRESAMPLE_VERSION_MAJOR -1 +// Opaque +typedef void* AVAudioResampleContext; +#endif +#ifndef LIBSWRESAMPLE_VERSION_MAJOR +#define LIBSWRESAMPLE_VERSION_MAJOR -1 +// Opaque +typedef struct SwrContext SwrContext; +#endif #include <stdarg.h> #include <stdio.h> #include <stdlib.h> +#include <GL/gl.h> + +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void); +typedef void (APIENTRYP PFNGLFLUSH) (void); +typedef void (APIENTRYP PFNGLFINISH) (void); + /** * AV_TIME_BASE 1000000 */ #define AV_TIME_BASE_MSEC (AV_TIME_BASE/1000) +#define AV_VERSION_MAJOR(i) ( ( i >> 16 ) & 0xFF ) +#define AV_VERSION_MINOR(i) ( ( i >> 8 ) & 0xFF ) +#define AV_VERSION_SUB(i) ( ( i >> 0 ) & 0xFF ) + +/** Sync w/ GLMediaPlayer.STREAM_ID_NONE */ +#define AV_STREAM_ID_NONE -2 + +/** Sync w/ GLMediaPlayer.STREAM_ID_AUTO */ +#define AV_STREAM_ID_AUTO -1 + +/** Default number of audio frames per video frame. Sync w/ FFMPEGMediaPlayer.AV_DEFAULT_AFRAMES. */ +#define AV_DEFAULT_AFRAMES 8 + +/** Constant PTS marking an invalid PTS, i.e. Integer.MIN_VALUE == 0x80000000 == {@value}. Sync w/ TimeFrameI.INVALID_PTS */ +#define INVALID_PTS 0x80000000 + +/** Constant PTS marking the end of the stream, i.e. Integer.MIN_VALUE - 1 == 0x7FFFFFFF == {@value}. Sync w/ TimeFrameI.END_OF_STREAM_PTS */ +#define END_OF_STREAM_PTS 0x7FFFFFFF + +/** Since 54.0.0.1 */ +#define AV_HAS_API_AVRESAMPLE(pAV) ( ( LIBAVRESAMPLE_VERSION_MAJOR >= 0 ) && ( pAV->avresampleVersion != 0 ) ) + +/** Since 55.0.0.1 */ +#define AV_HAS_API_SWRESAMPLE(pAV) ( ( LIBSWRESAMPLE_VERSION_MAJOR >= 0 ) && ( pAV->swresampleVersion != 0 ) ) + +#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; + return (float)a.num / (float)a.den; +} +static inline float my_av_q2f_r(AVRational a){ + return (float)a.den / (float)a.num; } -static inline int32_t my_av_q2i32(int32_t snum, AVRational a){ - return (snum * a.num) / a.den; +static inline int32_t my_av_q2i32(int64_t snum, AVRational a){ + return (int32_t) ( ( snum * (int64_t) a.num ) / (int64_t)a.den ); } +static inline int my_align(int v, int a){ + return ( v + a - 1 ) & ~( a - 1 ); +} + +typedef struct { + void *origPtr; + jobject nioRef; + int32_t size; +} NIOBuffer_t; + +typedef struct { + int64_t ptsError; // Number of backward PTS values (earlier than last PTS, excluding AV_NOPTS_VALUE) + int64_t dtsError; // Number of backward DTS values (earlier than last PTS, excluding AV_NOPTS_VALUE) + int64_t ptsLast; // PTS of the last frame + int64_t dtsLast; // DTS of the last frame +} PTSStats; typedef struct { + jobject ffmpegMediaPlayer; int32_t verbose; + uint32_t avcodecVersion; + uint32_t avformatVersion; + uint32_t avutilVersion; + uint32_t avresampleVersion; + uint32_t swresampleVersion; + + int32_t useRefCountedFrames; + + PFNGLTEXSUBIMAGE2DPROC procAddrGLTexSubImage2D; + PFNGLGETERRORPROC procAddrGLGetError; + PFNGLFLUSH procAddrGLFlush; + PFNGLFINISH procAddrGLFinish; + AVFormatContext* pFormatCtx; int32_t vid; AVStream* pVStream; @@ -74,26 +158,40 @@ typedef struct { uint32_t vBytesPerPixelPerPlane; enum PixelFormat vPixFmt; // native decoder fmt int32_t vPTS; // msec - overall last video PTS - int32_t vLinesize[3]; // decoded video linesize in bytes for each plane - int32_t vTexWidth[3]; // decoded video tex width in bytes for each plane - + PTSStats vPTSStats; + int32_t vTexWidth[4]; // decoded video tex width in bytes for each plane (max 4) + int32_t vWidth; + int32_t vHeight; + jboolean vFlipped; // false: !GL-Orientation, true: GL-Orientation int32_t aid; AVStream* pAStream; AVCodecContext* pACodecCtx; AVCodec* pACodec; - AVFrame* pAFrame; + AVFrame** pAFrames; + NIOBuffer_t* pANIOBuffers; + int32_t aFrameCount; + int32_t aFrameCurrent; + int32_t aFrameSize; // in samples per channel! + enum AVSampleFormat aSampleFmt; // native decoder fmt int32_t aSampleRate; int32_t aChannels; - int32_t aFrameSize; - enum AVSampleFormat aSampleFmt; // native decoder fmt + int32_t aSinkSupport; // supported by AudioSink + AVAudioResampleContext* avResampleCtx; + struct SwrContext* swResampleCtx; + uint8_t* aResampleBuffer; + enum AVSampleFormat aSampleFmtOut; // out fmt + int32_t aChannelsOut; + int32_t aSampleRateOut; int32_t aPTS; // msec - overall last audio PTS + PTSStats aPTSStats; float fps; // frames per seconds int32_t bps_stream; // bits per seconds int32_t bps_video; // bits per seconds int32_t bps_audio; // bits per seconds - int32_t totalFrames; + int32_t frames_video; + int32_t frames_audio; int32_t duration; // msec int32_t start_time; // msec 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 deleted file mode 100644 index 647de15ef..000000000 --- a/src/jogl/native/libav/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c +++ /dev/null @@ -1,717 +0,0 @@ -/** - * Copyright 2012 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ - -#include "jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.h" - -#include "JoglCommon.h" -#include "ffmpeg_tool.h" -#include <libavutil/pixdesc.h> -#include <GL/gl.h> - -typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); - -static const char * const ClazzNameFFMPEGMediaPlayer = "jogamp/opengl/util/av/impl/FFMPEGMediaPlayer"; - -static jclass ffmpegMediaPlayerClazz = NULL; -static jmethodID jni_mid_updateAttributes1 = NULL; -static jmethodID jni_mid_updateAttributes2 = 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); - -static AVCODEC_VERSION sp_avcodec_version; -static AVFORMAT_VERSION sp_avformat_version; -static AVUTIL_VERSION sp_avutil_version; -// count: 3 - - -// libavcodec -typedef int (APIENTRYP AVCODEC_CLOSE)(AVCodecContext *avctx); -typedef void (APIENTRYP AVCODEC_STRING)(char *buf, int buf_size, AVCodecContext *enc, int encode); -typedef AVCodec *(APIENTRYP AVCODEC_FIND_DECODER)(enum CodecID id); -typedef int (APIENTRYP AVCODEC_OPEN2)(AVCodecContext *avctx, AVCodec *codec, AVDictionary **options); // 53.6.0 -typedef int (APIENTRYP AVCODEC_OPEN)(AVCodecContext *avctx, AVCodec *codec); -typedef AVFrame *(APIENTRYP AVCODEC_ALLOC_FRAME)(void); -typedef int (APIENTRYP AVCODEC_DEFAULT_GET_BUFFER)(AVCodecContext *s, AVFrame *pic); -typedef void (APIENTRYP AVCODEC_DEFAULT_RELEASE_BUFFER)(AVCodecContext *s, AVFrame *pic); -typedef void (APIENTRYP AV_FREE_PACKET)(AVPacket *pkt); -typedef int (APIENTRYP AVCODEC_DECODE_AUDIO4)(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, AVPacket *avpkt); // 53.25.0 -typedef int (APIENTRYP AVCODEC_DECODE_AUDIO3)(AVCodecContext *avctx, int16_t *samples, int *frame_size_ptr, AVPacket *avpkt); // 52.23.0 -typedef int (APIENTRYP AVCODEC_DECODE_VIDEO2)(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, AVPacket *avpkt); // 52.23.0 - -static AVCODEC_CLOSE sp_avcodec_close; -static AVCODEC_STRING sp_avcodec_string; -static AVCODEC_FIND_DECODER sp_avcodec_find_decoder; -static AVCODEC_OPEN2 sp_avcodec_open2; // 53.6.0 -static AVCODEC_OPEN sp_avcodec_open; -static AVCODEC_ALLOC_FRAME sp_avcodec_alloc_frame; -static AVCODEC_DEFAULT_GET_BUFFER sp_avcodec_default_get_buffer; -static AVCODEC_DEFAULT_RELEASE_BUFFER sp_avcodec_default_release_buffer; -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: 15 - -// libavutil -typedef void (APIENTRYP AV_FREE)(void *ptr); -typedef int (APIENTRYP AV_GET_BITS_PER_PIXEL)(const AVPixFmtDescriptor *pixdesc); -static const AVPixFmtDescriptor* sp_av_pix_fmt_descriptors; -static AV_FREE sp_av_free; -static AV_GET_BITS_PER_PIXEL sp_av_get_bits_per_pixel; -// count: 18 - -// libavformat -typedef AVFormatContext *(APIENTRYP AVFORMAT_ALLOC_CONTEXT)(void); -typedef void (APIENTRYP AVFORMAT_FREE_CONTEXT)(AVFormatContext *s); // 52.96.0 -typedef void (APIENTRYP AVFORMAT_CLOSE_INPUT)(AVFormatContext **s); // 53.17.0 -typedef void (APIENTRYP AV_CLOSE_INPUT_FILE)(AVFormatContext *s); -typedef void (APIENTRYP AV_REGISTER_ALL)(void); -typedef int (APIENTRYP AVFORMAT_OPEN_INPUT)(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options); -typedef void (APIENTRYP AV_DUMP_FORMAT)(AVFormatContext *ic, int index, const char *url, int is_output); -typedef int (APIENTRYP AV_READ_FRAME)(AVFormatContext *s, AVPacket *pkt); -typedef int (APIENTRYP AV_SEEK_FRAME)(AVFormatContext *s, int stream_index, int64_t timestamp, int flags); -typedef int (APIENTRYP AVFORMAT_NETWORK_INIT)(void); // 53.13.0 -typedef int (APIENTRYP AVFORMAT_NETWORK_DEINIT)(void); // 53.13.0 -typedef int (APIENTRYP AVFORMAT_FIND_STREAM_INFO)(AVFormatContext *ic, AVDictionary **options); // 53.3.0 -typedef int (APIENTRYP AV_FIND_STREAM_INFO)(AVFormatContext *ic); - -static AVFORMAT_ALLOC_CONTEXT sp_avformat_alloc_context; -static AVFORMAT_FREE_CONTEXT sp_avformat_free_context; // 52.96.0 -static AVFORMAT_CLOSE_INPUT sp_avformat_close_input; // 53.17.0 -static AV_CLOSE_INPUT_FILE sp_av_close_input_file; -static AV_REGISTER_ALL sp_av_register_all; -static AVFORMAT_OPEN_INPUT sp_avformat_open_input; -static AV_DUMP_FORMAT sp_av_dump_format; -static AV_READ_FRAME sp_av_read_frame; -static AV_SEEK_FRAME sp_av_seek_frame; -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: 31 - -#define SYMBOL_COUNT 31 - -JNIEXPORT jboolean JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGDynamicLibraryBundleInfo_initSymbols0 - (JNIEnv *env, jclass clazz, jobject jSymbols, jint count) -{ - int64_t* symbols; // jlong -> int64_t -> intptr_t -> FUNC_PTR - int i; - - if(SYMBOL_COUNT != count) { - fprintf(stderr, "FFMPEGDynamicLibraryBundleInfo.initSymbols0: Wrong symbol count: Expected %d, Is %d\n", - SYMBOL_COUNT, count); - return JNI_FALSE; - } - JoglCommon_init(env); - - i = 0; - symbols = (int64_t *) (*env)->GetPrimitiveArrayCritical(env, jSymbols, NULL); - - 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_avcodec_close = (AVCODEC_CLOSE) (intptr_t) symbols[i++]; - sp_avcodec_string = (AVCODEC_STRING) (intptr_t) symbols[i++]; - sp_avcodec_find_decoder = (AVCODEC_FIND_DECODER) (intptr_t) symbols[i++]; - sp_avcodec_open2 = (AVCODEC_OPEN2) (intptr_t) symbols[i++]; - sp_avcodec_open = (AVCODEC_OPEN) (intptr_t) symbols[i++]; - sp_avcodec_alloc_frame = (AVCODEC_ALLOC_FRAME) (intptr_t) symbols[i++]; - sp_avcodec_default_get_buffer = (AVCODEC_DEFAULT_GET_BUFFER) (intptr_t) symbols[i++]; - sp_avcodec_default_release_buffer = (AVCODEC_DEFAULT_RELEASE_BUFFER) (intptr_t) symbols[i++]; - sp_av_free_packet = (AV_FREE_PACKET) (intptr_t) symbols[i++]; - 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: 15 - - sp_av_pix_fmt_descriptors = (const AVPixFmtDescriptor*) (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++]; - // count: 18 - - sp_avformat_alloc_context = (AVFORMAT_ALLOC_CONTEXT) (intptr_t) symbols[i++];; - sp_avformat_free_context = (AVFORMAT_FREE_CONTEXT) (intptr_t) symbols[i++]; - sp_avformat_close_input = (AVFORMAT_CLOSE_INPUT) (intptr_t) symbols[i++]; - sp_av_close_input_file = (AV_CLOSE_INPUT_FILE) (intptr_t) symbols[i++]; - sp_av_register_all = (AV_REGISTER_ALL) (intptr_t) symbols[i++]; - sp_avformat_open_input = (AVFORMAT_OPEN_INPUT) (intptr_t) symbols[i++]; - sp_av_dump_format = (AV_DUMP_FORMAT) (intptr_t) symbols[i++]; - sp_av_read_frame = (AV_READ_FRAME) (intptr_t) symbols[i++]; - sp_av_seek_frame = (AV_SEEK_FRAME) (intptr_t) symbols[i++]; - sp_avformat_network_init = (AVFORMAT_NETWORK_INIT) (intptr_t) symbols[i++]; - 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: 31 - - (*env)->ReleasePrimitiveArrayCritical(env, jSymbols, symbols, 0); - - if(SYMBOL_COUNT != i) { - // boom - fprintf(stderr, "FFMPEGDynamicLibraryBundleInfo.initSymbols0: Wrong symbol assignment count: Expected %d, Is %d\n", - SYMBOL_COUNT, i); - return JNI_FALSE; - } - - return JNI_TRUE; -} - -static void _updateJavaAttributes(JNIEnv *env, jobject instance, FFMPEGToolBasicAV_t* pAV) -{ - // int shallBeDetached = 0; - // JNIEnv * env = JoglCommon_GetJNIEnv (&shallBeDetached); - if(NULL!=env) { - int32_t w, h; - if( NULL != pAV->pVCodecCtx ) { - // FIXME: Libav Binary compatibility! JAU01 - w = pAV->pVCodecCtx->width; h = pAV->pVCodecCtx->height; - } else { - w = 0; h = 0; - } - - (*env)->CallVoidMethod(env, instance, jni_mid_updateAttributes1, - w, h, - pAV->bps_stream, pAV->bps_video, pAV->bps_audio, - pAV->fps, (int32_t)((pAV->duration/1000)*pAV->fps), pAV->duration, - (*env)->NewStringUTF(env, pAV->vcodec), - (*env)->NewStringUTF(env, pAV->acodec) ); - (*env)->CallVoidMethod(env, instance, jni_mid_updateAttributes2, - pAV->vPixFmt, pAV->vBufferPlanes, - pAV->vBitsPerPixel, pAV->vBytesPerPixelPerPlane, - pAV->vLinesize[0], pAV->vLinesize[1], pAV->vLinesize[2], - pAV->vTexWidth[0], pAV->vTexWidth[1], pAV->vTexWidth[2]); - // JoglCommon_ReleaseJNIEnv (shallBeDetached); - } -} - -static void freeInstance(FFMPEGToolBasicAV_t* pAV) { - int i; - if(NULL != pAV) { - // Close the V codec - if(NULL != pAV->pVCodecCtx) { - sp_avcodec_close(pAV->pVCodecCtx); - pAV->pVCodecCtx = NULL; - } - pAV->pVCodec=NULL; - - // Close the A codec - if(NULL != pAV->pACodecCtx) { - sp_avcodec_close(pAV->pACodecCtx); - pAV->pACodecCtx = NULL; - } - pAV->pACodec=NULL; - - // Close the frames - if(NULL != pAV->pVFrame) { - sp_av_free(pAV->pVFrame); - pAV->pVFrame = NULL; - } - if(NULL != pAV->pAFrame) { - sp_av_free(pAV->pAFrame); - pAV->pAFrame = NULL; - } - - // Close the video file - if(NULL != pAV->pFormatCtx) { - if(HAS_FUNC(sp_avformat_close_input)) { - sp_avformat_close_input(&pAV->pFormatCtx); - } else { - sp_av_close_input_file(pAV->pFormatCtx); - if(HAS_FUNC(sp_avformat_free_context)) { - sp_avformat_free_context(pAV->pFormatCtx); - } - } - pAV->pFormatCtx = NULL; - } - free(pAV); - } -} - -static int my_getPlaneCount(AVPixFmtDescriptor *pDesc) { - int i, p=-1; - for(i=pDesc->nb_components-1; i>=0; i--) { - int p0 = pDesc->comp[i].plane; - if( p < p0 ) { - p = p0; - } - } - return p+1; -} - -static int my_is_hwaccel_pix_fmt(enum PixelFormat pix_fmt) { - return sp_av_pix_fmt_descriptors[pix_fmt].flags & PIX_FMT_HWACCEL; -} - -#if 0 -static enum PixelFormat my_get_format(struct AVCodecContext *s, const enum PixelFormat * fmt) { - int i=0; - enum PixelFormat f0, fR = PIX_FMT_NONE; - char buf[256]; - - fprintf(stderr, "get_format ****\n"); - while (fmt[i] != PIX_FMT_NONE /* && ff_is_hwaccel_pix_fmt(fmt[i]) */) { - f0 = fmt[i]; - if(fR==PIX_FMT_NONE && !my_is_hwaccel_pix_fmt(f0)) { - fR = f0; - } - sp_av_get_pix_fmt_string(buf, sizeof(buf), f0); - fprintf(stderr, "get_format %d: %d - %s - %s\n", i, f0, sp_av_get_pix_fmt_name(f0), buf); - ++i; - } - fprintf(stderr, "get_format %d - %s *** \n", fR, sp_av_get_pix_fmt_name(fR)); - fflush(NULL); - return fR; -} -#endif - -JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_getAvUtilVersion0 - (JNIEnv *env, jclass clazz) { - return (jint) sp_avutil_version(); -} - -JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_getAvFormatVersion0 - (JNIEnv *env, jclass clazz) { - return (jint) sp_avformat_version(); -} - -JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_getAvCodecVersion0 - (JNIEnv *env, jclass clazz) { - return (jint) sp_avcodec_version(); -} - -JNIEXPORT jboolean JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_initIDs0 - (JNIEnv *env, jclass clazz) -{ - JoglCommon_init(env); - - jclass c; - if (ffmpegMediaPlayerClazz != NULL) { - return; - } - - c = (*env)->FindClass(env, ClazzNameFFMPEGMediaPlayer); - if(NULL==c) { - JoglCommon_FatalError(env, "JOGL FFMPEG: can't find %s", ClazzNameFFMPEGMediaPlayer); - } - ffmpegMediaPlayerClazz = (jclass)(*env)->NewGlobalRef(env, c); - (*env)->DeleteLocalRef(env, c); - if(NULL==ffmpegMediaPlayerClazz) { - JoglCommon_FatalError(env, "JOGL FFMPEG: can't use %s", ClazzNameFFMPEGMediaPlayer); - } - - jni_mid_updateAttributes1 = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "updateAttributes", "(IIIIIFIILjava/lang/String;Ljava/lang/String;)V"); - jni_mid_updateAttributes2 = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "updateAttributes2", "(IIIIIIIIII)V"); - - if(jni_mid_updateAttributes1 == NULL || - jni_mid_updateAttributes2 == NULL) { - return JNI_FALSE; - } - return JNI_TRUE; -} - -JNIEXPORT jlong JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_createInstance0 - (JNIEnv *env, jobject instance, jboolean verbose) -{ - FFMPEGToolBasicAV_t * pAV = calloc(1, sizeof(FFMPEGToolBasicAV_t)); - if(NULL==pAV) { - JoglCommon_throwNewRuntimeException(env, "Couldn't alloc instance"); - return 0; - } - // Register all formats and codecs - sp_av_register_all(); - // Network too .. - if(HAS_FUNC(sp_avformat_network_init)) { - sp_avformat_network_init(); - } - - pAV->verbose = verbose; - pAV->vid=-1; - pAV->aid=-1; - - return (jlong) (intptr_t) pAV; -} - -JNIEXPORT void JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_destroyInstance0 - (JNIEnv *env, jobject instance, jlong ptr) -{ - FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); - if (pAV != NULL) { - // stop assumed .. - freeInstance(pAV); - } -} - -JNIEXPORT void JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_setStream0 - (JNIEnv *env, jobject instance, jlong ptr, jstring jURL, jint vid, jint aid) -{ - int res, i; - jboolean iscopy; - FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)(intptr_t)ptr; - - if (pAV == NULL) { - JoglCommon_throwNewRuntimeException(env, "NULL AV ptr"); - return; - } - - pAV->pFormatCtx = sp_avformat_alloc_context(); - - // Open video file - const char *urlPath = (*env)->GetStringUTFChars(env, jURL, &iscopy); - res = sp_avformat_open_input(&pAV->pFormatCtx, urlPath, NULL, NULL); - if(res != 0) { - (*env)->ReleaseStringChars(env, jURL, (const jchar *)urlPath); - JoglCommon_throwNewRuntimeException(env, "Couldn't open URL"); - return; - } - - // Retrieve detailed stream information - if(HAS_FUNC(sp_avformat_find_stream_info)) { - if(sp_avformat_find_stream_info(pAV->pFormatCtx, NULL)<0) { - (*env)->ReleaseStringChars(env, jURL, (const jchar *)urlPath); - JoglCommon_throwNewRuntimeException(env, "Couldn't find stream information"); - return; - } - } else { - if(sp_av_find_stream_info(pAV->pFormatCtx)<0) { - (*env)->ReleaseStringChars(env, jURL, (const jchar *)urlPath); - JoglCommon_throwNewRuntimeException(env, "Couldn't find stream information"); - return; - } - } - - if(pAV->verbose) { - // Dump information about file onto standard error - sp_av_dump_format(pAV->pFormatCtx, 0, urlPath, JNI_FALSE); - } - (*env)->ReleaseStringChars(env, jURL, (const jchar *)urlPath); - // FIXME: Libav Binary compatibility! JAU01 - if (pAV->pFormatCtx->duration != AV_NOPTS_VALUE) { - pAV->duration = pAV->pFormatCtx->duration / AV_TIME_BASE_MSEC; - } - if (pAV->pFormatCtx->start_time != AV_NOPTS_VALUE) { - pAV->start_time = pAV->pFormatCtx->start_time / AV_TIME_BASE_MSEC; - } - if (pAV->pFormatCtx->bit_rate) { - pAV->bps_stream = pAV->pFormatCtx->bit_rate; - } - - fprintf(stderr, "Streams: %d\n", pAV->pFormatCtx->nb_streams); // JAU - - // Find the first audio and video stream, or the one matching vid - // FIXME: Libav Binary compatibility! JAU01 - for(i=0; ( -1==pAV->aid || -1==pAV->vid ) && i<pAV->pFormatCtx->nb_streams; i++) { - AVStream *st = pAV->pFormatCtx->streams[i]; - fprintf(stderr, "Stream: %d: is-video %d, is-audio %d\n", i, (AVMEDIA_TYPE_VIDEO == st->codec->codec_type), AVMEDIA_TYPE_AUDIO == st->codec->codec_type); // JAU - if(AVMEDIA_TYPE_VIDEO == st->codec->codec_type) { - if(-1==pAV->vid && (-1==vid || vid == i) ) { - pAV->pVStream = st; - pAV->vid=i; - } - } else if(AVMEDIA_TYPE_AUDIO == st->codec->codec_type) { - if(-1==pAV->aid && (-1==aid || aid == i) ) { - pAV->pAStream = st; - pAV->aid=i; - } - } - } - - fprintf(stderr, "Found vid %d, aid %d\n", pAV->vid, pAV->aid); // JAU - - if(0<=pAV->aid) { - // Get a pointer to the codec context for the audio stream - // FIXME: Libav Binary compatibility! JAU01 - pAV->pACodecCtx=pAV->pAStream->codec; - - // FIXME: Libav Binary compatibility! JAU01 - if (pAV->pACodecCtx->bit_rate) { - pAV->bps_audio = pAV->pACodecCtx->bit_rate; - } - sp_avcodec_string(pAV->acodec, sizeof(pAV->acodec), pAV->pACodecCtx, 0); - - // Find the decoder for the audio stream - pAV->pACodec=sp_avcodec_find_decoder(pAV->pACodecCtx->codec_id); - if(pAV->pACodec==NULL) { - JoglCommon_throwNewRuntimeException(env, "Couldn't find audio codec %d, %s", pAV->pACodecCtx->codec_id, pAV->acodec); - return; - } - - // Open codec - if(HAS_FUNC(sp_avcodec_open2)) { - res = sp_avcodec_open2(pAV->pACodecCtx, pAV->pACodec, NULL); - } else { - res = sp_avcodec_open(pAV->pACodecCtx, pAV->pACodec); - } - if(res<0) { - JoglCommon_throwNewRuntimeException(env, "Couldn't open audio codec %d, %s", pAV->pACodecCtx->codec_id, pAV->acodec); - return; - } - - // Allocate audio frames - // FIXME: Libav Binary compatibility! JAU01 - pAV->aSampleRate = pAV->pACodecCtx->sample_rate; - pAV->aChannels = pAV->pACodecCtx->channels; - pAV->aFrameSize = pAV->pACodecCtx->frame_size; - pAV->aSampleFmt = pAV->pACodecCtx->sample_fmt; - pAV->pAFrame=sp_avcodec_alloc_frame(); - if(pAV->pAFrame==NULL) { - JoglCommon_throwNewRuntimeException(env, "Couldn't alloc audio frame"); - return; - } - } - - if(0<=pAV->vid) { - // Get a pointer to the codec context for the video stream - // FIXME: Libav Binary compatibility! JAU01 - pAV->pVCodecCtx=pAV->pVStream->codec; - #if 0 - pAV->pVCodecCtx->get_format = my_get_format; - #endif - - if (pAV->pVCodecCtx->bit_rate) { - // FIXME: Libav Binary compatibility! JAU01 - pAV->bps_video = pAV->pVCodecCtx->bit_rate; - } - sp_avcodec_string(pAV->vcodec, sizeof(pAV->vcodec), pAV->pVCodecCtx, 0); - - // Find the decoder for the video stream - pAV->pVCodec=sp_avcodec_find_decoder(pAV->pVCodecCtx->codec_id); - if(pAV->pVCodec==NULL) { - JoglCommon_throwNewRuntimeException(env, "Couldn't find video codec %d, %s", pAV->pVCodecCtx->codec_id, pAV->vcodec); - return; - } - - // Open codec - if(HAS_FUNC(sp_avcodec_open2)) { - res = sp_avcodec_open2(pAV->pVCodecCtx, pAV->pVCodec, NULL); - } else { - res = sp_avcodec_open(pAV->pVCodecCtx, pAV->pVCodec); - } - if(res<0) { - JoglCommon_throwNewRuntimeException(env, "Couldn't open video codec %d, %s", pAV->pVCodecCtx->codec_id, pAV->vcodec); - return; - } - - // Hack to correct wrong frame rates that seem to be generated by some codecs - // FIXME: Libav Binary compatibility! JAU01 - if(pAV->pVCodecCtx->time_base.num>1000 && pAV->pVCodecCtx->time_base.den==1) { - pAV->pVCodecCtx->time_base.den=1000; - } - // FIXME: Libav Binary compatibility! JAU01 - pAV->fps = my_av_q2f(pAV->pVStream->avg_frame_rate); - - // Allocate video frames - // FIXME: Libav Binary compatibility! JAU01 - pAV->vPixFmt = pAV->pVCodecCtx->pix_fmt; - { - AVPixFmtDescriptor pixDesc = sp_av_pix_fmt_descriptors[pAV->vPixFmt]; - pAV->vBitsPerPixel = sp_av_get_bits_per_pixel(&pixDesc); - pAV->vBufferPlanes = my_getPlaneCount(&pixDesc); - } - pAV->pVFrame=sp_avcodec_alloc_frame(); - if( pAV->pVFrame == NULL ) { - JoglCommon_throwNewRuntimeException(env, "Couldn't alloc video frame"); - return; - } - res = sp_avcodec_default_get_buffer(pAV->pVCodecCtx, pAV->pVFrame); - if(0==res) { - const int32_t bytesPerPixel = ( pAV->vBitsPerPixel + 7 ) / 8 ; - if(1 == pAV->vBufferPlanes) { - pAV->vBytesPerPixelPerPlane = bytesPerPixel; - } else { - pAV->vBytesPerPixelPerPlane = 1; - } - for(i=0; i<3; i++) { - // FIXME: Libav Binary compatibility! JAU01 - pAV->vLinesize[i] = pAV->pVFrame->linesize[i]; - pAV->vTexWidth[i] = pAV->vLinesize[i] / pAV->vBytesPerPixelPerPlane ; - } - sp_avcodec_default_release_buffer(pAV->pVCodecCtx, pAV->pVFrame); - } else { - JoglCommon_throwNewRuntimeException(env, "Couldn't peek video buffer dimension"); - return; - } - } - pAV->vPTS=0; - pAV->aPTS=0; - _updateJavaAttributes(env, instance, pAV); -} - -JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_readNextPacket0 - (JNIEnv *env, jobject instance, jlong ptr, jlong jProcAddrGLTexSubImage2D, jint texTarget, jint texFmt, jint texType) -{ - FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); - PFNGLTEXSUBIMAGE2DPROC procAddrGLTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) jProcAddrGLTexSubImage2D; - - jint res = 0; // 1 - audio, 2 - video - AVPacket packet; - int frameFinished; - - if(sp_av_read_frame(pAV->pFormatCtx, &packet)>=0) { - /** - if(packet.stream_index==pAV->aid) { - // Decode audio frame - if(NULL == pAV->pAFrame) { - sp_av_free_packet(&packet); - return res; - } - - int new_packet = 1; - int len1; - int flush_complete = 0; - int data_size = 0; - while (packet.size > 0 || (!packet.data && new_packet)) { - new_packet = 0; - if (flush_complete) { - break; - } - if(HAS_FUNC(sp_avcodec_decode_audio4)) { - len1 = sp_avcodec_decode_audio4(pAV->pVCodecCtx, pAV->pAFrame, &frameFinished, &packet); - } else { - len1 = sp_avcodec_decode_audio3(pAV->pVCodecCtx, int16_t *samples, int *frame_size_ptr, &frameFinished, &packet); - } - if (len1 < 0) { - // if error, we skip the frame - packet.size = 0; - break; - } - packet.data += len1; - packet.size -= len1; - - if (!frameFinished) { - // stop sending empty packets if the decoder is finished - if (!packet.data && pAV->pVCodecCtx->codec->capabilities & CODEC_CAP_DELAY) { - flush_complete = 1; - } - continue; - } - - int32_t pts = pAV->pAFrame->pkt_pts * my_av_q2i32(1000, pAV->pAStream->time_base); - pAV->aPTS += ( data_size * 1000 ) / (2 * pAV->pVCodecCtx->channels * pAV->pVCodecCtx->sample_rate); - printf("A pts %d - %d\n", pts, pAV->aPTS); - res = 1; - } - } else */ if(packet.stream_index==pAV->vid) { - // Decode video frame - if(NULL == pAV->pVFrame) { - sp_av_free_packet(&packet); - return res; - } - sp_avcodec_decode_video2(pAV->pVCodecCtx, pAV->pVFrame, &frameFinished, &packet); - - // Did we get a video frame? - if(frameFinished) - { - res = 2; - // FIXME: Libav Binary compatibility! JAU01 - const AVRational time_base = pAV->pVStream->time_base; - const int64_t pts = pAV->pVFrame->pkt_pts; - if(AV_NOPTS_VALUE != pts) { // discard invalid PTS .. - pAV->vPTS = pts * my_av_q2i32(1000, time_base); - - #if 0 - printf("PTS %d = %ld * ( ( 1000 * %ld ) / %ld ) '1000 * time_base', time_base = %lf\n", - pAV->vPTS, pAV->pVFrame->pkt_pts, time_base.num, time_base.den, (time_base.num/(double)time_base.den)); - #endif - } - - #if 0 - printf("tex2D codec %dx%d - frame %dx%d - width %d tex / %d linesize, pixfmt 0x%X, texType 0x%x, texTarget 0x%x\n", - pAV->pVCodecCtx->width, pAV->pVCodecCtx->height, - pAV->pVFrame->width, pAV->pVFrame->height, pAV->vTexWidth[0], pAV->pVFrame->linesize[0], - texFmt, texType, texTarget); - #endif - - // 1st plane or complete packed frame - // FIXME: Libav Binary compatibility! JAU01 - procAddrGLTexSubImage2D(texTarget, 0, - 0, 0, - pAV->vTexWidth[0], pAV->pVCodecCtx->height, - texFmt, texType, pAV->pVFrame->data[0]); - - if(pAV->vPixFmt == PIX_FMT_YUV420P) { - // U plane - // FIXME: Libav Binary compatibility! JAU01 - procAddrGLTexSubImage2D(texTarget, 0, - pAV->pVCodecCtx->width, 0, - pAV->vTexWidth[1], pAV->pVCodecCtx->height/2, - texFmt, texType, pAV->pVFrame->data[1]); - // V plane - // FIXME: Libav Binary compatibility! JAU01 - procAddrGLTexSubImage2D(texTarget, 0, - pAV->pVCodecCtx->width, pAV->pVCodecCtx->height/2, - pAV->vTexWidth[2], pAV->pVCodecCtx->height/2, - texFmt, texType, pAV->pVFrame->data[2]); - } // FIXME: Add more planar formats ! - } - } - - // Free the packet that was allocated by av_read_frame - sp_av_free_packet(&packet); - } - return res; -} - -JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_seek0 - (JNIEnv *env, jobject instance, jlong ptr, jint pos1) -{ - FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); - int64_t pos0 = pAV->vPTS; - int64_t pts0 = pAV->pVFrame->pkt_pts; - int64_t pts1 = (int64_t) pos1 / my_av_q2i32(1000, pAV->pVStream->time_base); - int flags = 0; - if(pos1 < pos0) { - flags |= AVSEEK_FLAG_BACKWARD; - } - fprintf(stderr, "SEEK: pre : u %d, p %d -> u %d, p %d\n", pos0, pts0, pos1, pts1); - sp_av_seek_frame(pAV->pFormatCtx, pAV->vid, pts1, flags); - pAV->vPTS = pAV->pVFrame->pkt_pts * my_av_q2i32(1000, pAV->pVStream->time_base); - fprintf(stderr, "SEEK: post : u %d, p %d\n", pAV->vPTS, pAV->pVFrame->pkt_pts); - return pAV->vPTS; -} - -JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_getVideoPTS0 - (JNIEnv *env, jobject instance, jlong ptr) -{ - FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); - return pAV->vPTS; -} - -JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_getAudioPTS0 - (JNIEnv *env, jobject instance, jlong ptr) -{ - FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); - return pAV->aPTS; -} - |