ffmpeg support on linux, and check compiled vs. runtime versions of ffmpeg
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
build
|
build
|
||||||
lib/linux-x86_64
|
lib/linux-x86_64
|
||||||
|
build-ffmpeg
|
||||||
|
|
||||||
|
|
||||||
/ffmpeg-audio/.qtcreator
|
/ffmpeg-audio/.qtcreator
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
|
|
||||||
|
SUBDIR := $(shell racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))")
|
||||||
|
|
||||||
all:
|
all:
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cmake -S ao-play-async -B build
|
cmake -S ao-play-async -B build
|
||||||
(cd build; make)
|
(cd build; make)
|
||||||
|
cmake -S ffmpeg-audio -B build-ffmpeg
|
||||||
|
(cd build-ffmpeg; make)
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
SUBDIR=`racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))"`; \
|
mkdir -p lib/$(SUBDIR)
|
||||||
mkdir -p lib/$$SUBDIR
|
FILES=`ls build/*.so build-ffmpeg/*.so` 2>/dev/null; if [ "$$FILES" != "" ]; then cp $$FILES lib/$(SUBDIR); fi
|
||||||
SUBDIR=`racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))"`; \
|
FILES=`ls build/*.dll build-ffmpeg/*.dll` 2>/dev/null; if [ "$$FILES" != "" ]; then cp $$FILES lib/$(SUBDIR); fi
|
||||||
FILES=`ls build/*.so` 2>/dev/null; if [ "$$FILES" != "" ]; then cp $$FILES lib/$$SUBDIR; fi
|
|
||||||
SUBDIR=`racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))"`; \
|
|
||||||
FILES=`ls build/*.dll` 2>/dev/null; if [ "$$FILES" != "" ]; then cp $$FILES lib/$$SUBDIR; fi
|
|
||||||
|
|
||||||
test: install
|
test: install
|
||||||
cp lib/linux-x86_64/*.so ~/.local/share/racket/racket-sound-lib/linux-x86_64
|
cp lib/linux-x86_64/*.so ~/.local/share/racket/racket-sound-lib/linux-x86_64
|
||||||
|
|
||||||
zip: install
|
zip: install
|
||||||
SUBDIR=`racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))"`; \
|
(cd lib; zip -y -r -9 $(SUBDIR).zip $(SUBDIR))
|
||||||
(cd lib; zip -y -r -9 $$SUBDIR.zip $$SUBDIR)
|
(cd lib; rm -rf $(SUBDIR))
|
||||||
SUBDIR=`racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))"`; \
|
|
||||||
(cd lib; rm -rf $$SUBDIR)
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build
|
rm -rf build
|
||||||
|
rm -rf build-ffmpeg
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+148
-11
@@ -100,7 +100,7 @@ typedef struct _queue_ {
|
|||||||
struct _queue_ *prev;
|
struct _queue_ *prev;
|
||||||
} Queue_t;
|
} Queue_t;
|
||||||
|
|
||||||
typedef int(*ao_play_func_t)(void *, char *, uint32_t);
|
struct AO_Handle;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Queue_t *play_queue;
|
Queue_t *play_queue;
|
||||||
@@ -109,6 +109,7 @@ typedef struct {
|
|||||||
int paused;
|
int paused;
|
||||||
|
|
||||||
ao_device *ao_device;
|
ao_device *ao_device;
|
||||||
|
int requested_bits_per_sample;
|
||||||
int dev_bits_per_sample;
|
int dev_bits_per_sample;
|
||||||
int dev_endianess;
|
int dev_endianess;
|
||||||
int dev_channels;
|
int dev_channels;
|
||||||
@@ -348,9 +349,131 @@ static void init_ao()
|
|||||||
if (!init) {
|
if (!init) {
|
||||||
ao_initialize();
|
ao_initialize();
|
||||||
atexit(at_exit_shutdown_ao);
|
atexit(at_exit_shutdown_ao);
|
||||||
|
init = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ao_device *try_open_device(int bits, int rate, int channels, int byte_format,
|
||||||
|
const char *wav_file_output,
|
||||||
|
int *opened_bits)
|
||||||
|
{
|
||||||
|
int candidates[3];
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
candidates[n++] = bits;
|
||||||
|
|
||||||
|
if (bits > 24) candidates[n++] = 24;
|
||||||
|
if (bits > 16) candidates[n++] = 16;
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
ao_sample_format fmt;
|
||||||
|
fmt.bits = candidates[i];
|
||||||
|
fmt.rate = rate;
|
||||||
|
fmt.byte_format = byte_format;
|
||||||
|
fmt.channels = channels;
|
||||||
|
fmt.matrix = NULL;
|
||||||
|
|
||||||
|
ao_device *dev;
|
||||||
|
|
||||||
|
if (wav_file_output != NULL) {
|
||||||
|
int driver_id = ao_driver_id("wav");
|
||||||
|
dev = ao_open_file(driver_id, wav_file_output, 1, &fmt, NULL);
|
||||||
|
} else {
|
||||||
|
dev = ao_open_live(ao_default_driver_id(), &fmt, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev != NULL) {
|
||||||
|
*opened_bits = candidates[i];
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline int32_t convert_bits(int32_t sample, int req_bits, int out_bits)
|
||||||
|
{
|
||||||
|
if (req_bits > out_bits) {
|
||||||
|
sample >>= (req_bits - out_bits);
|
||||||
|
} else if (req_bits < out_bits) {
|
||||||
|
sample <<= (out_bits - req_bits);
|
||||||
|
}
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int32_t read_sample(unsigned char *mem, int req_bytes, int little_endian)
|
||||||
|
{
|
||||||
|
uint32_t v = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < req_bytes; i++) {
|
||||||
|
int idx = little_endian ? i : (req_bytes - i - 1);
|
||||||
|
v |= ((uint32_t) mem[idx]) << (8 * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_bytes < 4) {
|
||||||
|
int bits = req_bytes * 8;
|
||||||
|
uint32_t sign = 1u << (bits - 1);
|
||||||
|
v = (v ^ sign) - sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int32_t) v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void store_sample(unsigned char *dst, int32_t sample, int out_bytes, int is_little_endian)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < out_bytes; i++) {
|
||||||
|
int idx = is_little_endian ? i : (out_bytes - i - 1);
|
||||||
|
dst[idx] = sample & 0xff;
|
||||||
|
sample >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *convert_req_to_real(AO_Handle *h, void *mem, int mem_size, BufferInfo_t *info, int *out_size)
|
||||||
|
{
|
||||||
|
int endianess = h->dev_endianess;
|
||||||
|
int little_endian = (endianess == AO_FMT_LITTLE);
|
||||||
|
if (!little_endian && endianess == AO_FMT_NATIVE) little_endian = littleEndian();
|
||||||
|
|
||||||
|
int requested_bits = h->requested_bits_per_sample;
|
||||||
|
int output_bits = h->dev_bits_per_sample;
|
||||||
|
|
||||||
|
int req_bytes = (requested_bits / 8);
|
||||||
|
int out_bytes = (output_bits / 8);
|
||||||
|
|
||||||
|
int samples = (mem_size / req_bytes);
|
||||||
|
|
||||||
|
unsigned char *buf_out = (unsigned char *) malloc(samples * out_bytes);
|
||||||
|
if (buf_out == NULL) {
|
||||||
|
fprintf(stderr, "Allocation of output buffer of %d samples of %d bits gives NULL", samples, output_bits);
|
||||||
|
*out_size = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requested_bits == output_bits) {
|
||||||
|
*out_size = mem_size;
|
||||||
|
memcpy(buf_out, mem, mem_size);
|
||||||
|
} else {
|
||||||
|
// convert samples.
|
||||||
|
|
||||||
|
int sample;
|
||||||
|
unsigned char *p_in = (unsigned char *) mem;
|
||||||
|
unsigned char *p_out = buf_out;
|
||||||
|
|
||||||
|
for(sample = 0; sample < samples; sample++, p_in += req_bytes, p_out += out_bytes) {
|
||||||
|
register int32_t smpl;
|
||||||
|
smpl = read_sample(p_in, req_bytes, little_endian);
|
||||||
|
int32_t out_smpl = convert_bits(smpl, requested_bits, output_bits);
|
||||||
|
store_sample(p_out, out_smpl, out_bytes, little_endian);
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_size = samples * out_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf_out;
|
||||||
|
}
|
||||||
|
|
||||||
void *ao_create_async(int bits, int rate, int channels, int byte_format, const char *wav_file_output)
|
void *ao_create_async(int bits, int rate, int channels, int byte_format, const char *wav_file_output)
|
||||||
{
|
{
|
||||||
init_ao();
|
init_ao();
|
||||||
@@ -364,21 +487,18 @@ void *ao_create_async(int bits, int rate, int channels, int byte_format, const c
|
|||||||
fmt.channels = channels;
|
fmt.channels = channels;
|
||||||
fmt.matrix = NULL;
|
fmt.matrix = NULL;
|
||||||
|
|
||||||
ao_device *dev;
|
int opened_bits = 0;
|
||||||
if (wav_file_output != NULL) {
|
ao_device *dev = try_open_device(bits, rate, channels, byte_format, wav_file_output, &opened_bits);
|
||||||
int driver_id = ao_driver_id("wav");
|
|
||||||
dev = ao_open_file(driver_id, wav_file_output, 1, &fmt, NULL);
|
|
||||||
} else {
|
|
||||||
dev = ao_open_live(ao_default_driver_id(), &fmt, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev == NULL) {
|
if (dev == NULL) {
|
||||||
fprintf(stderr, "Cannot open ao-device, error code = %d\n", errno);
|
fprintf(stderr, "Cannot open ao-device, error code = %d\n", errno);
|
||||||
|
free(handle);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle->ao_device = dev;
|
handle->ao_device = dev;
|
||||||
handle->dev_bits_per_sample = bits;
|
handle->requested_bits_per_sample = bits;
|
||||||
|
handle->dev_bits_per_sample = opened_bits;
|
||||||
handle->dev_channels = channels;
|
handle->dev_channels = channels;
|
||||||
handle->dev_rate = rate;
|
handle->dev_rate = rate;
|
||||||
handle->dev_endianess = byte_format;
|
handle->dev_endianess = byte_format;
|
||||||
@@ -563,12 +683,23 @@ void ao_play_async(void *ao_handle, int music_id, double at_second, double music
|
|||||||
case flac: {
|
case flac: {
|
||||||
int store_size = 0;
|
int store_size = 0;
|
||||||
void *store_mem = convertFlac(mem, buf_size, &info, &store_size);
|
void *store_mem = convertFlac(mem, buf_size, &info, &store_size);
|
||||||
q = new_elem(PLAY, music_id, at_second, music_duration, store_size, store_mem);
|
|
||||||
|
int ao_size = 0;
|
||||||
|
void *ao_mem = convert_req_to_real(ao_handle, store_mem, store_size, &info, &ao_size);
|
||||||
|
|
||||||
|
q = new_elem(PLAY, music_id, at_second, music_duration, ao_size, ao_mem);
|
||||||
|
|
||||||
free(store_mem);
|
free(store_mem);
|
||||||
|
free(ao_mem);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ao: {
|
case ao: {
|
||||||
q = new_elem(PLAY, music_id, at_second, music_duration, buf_size, mem);
|
int ao_size = 0;
|
||||||
|
void *ao_mem = convert_req_to_real(ao_handle, mem, buf_size, &info, &ao_size);
|
||||||
|
|
||||||
|
q = new_elem(PLAY, music_id, at_second, music_duration, ao_size, ao_mem);
|
||||||
|
|
||||||
|
free(ao_mem);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case mpg123: {
|
case mpg123: {
|
||||||
@@ -680,3 +811,9 @@ void ao_pause_async(void *ao_handle, int paused)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int ao_real_output_bits_async(void *handle)
|
||||||
|
{
|
||||||
|
AO_Handle *h = (AO_Handle *) handle;
|
||||||
|
return h->dev_bits_per_sample;
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,5 +44,6 @@ AOPLAYASYNC_EXPORT void ao_set_volume_async(void *ao_handle, double percentage);
|
|||||||
AOPLAYASYNC_EXPORT double ao_volume_async(void *ao_handle);
|
AOPLAYASYNC_EXPORT double ao_volume_async(void *ao_handle);
|
||||||
|
|
||||||
AOPLAYASYNC_EXPORT int ao_bufsize_async(void *handle);
|
AOPLAYASYNC_EXPORT int ao_bufsize_async(void *handle);
|
||||||
|
AOPLAYASYNC_EXPORT int ao_real_output_bits_async(void *handle);
|
||||||
|
|
||||||
#endif // AO_PLAYASYNC_H
|
#endif // AO_PLAYASYNC_H
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ if(WIN32)
|
|||||||
target_link_directories(ffmpeg_audio PRIVATE ${FFMPEG_LIB})
|
target_link_directories(ffmpeg_audio PRIVATE ${FFMPEG_LIB})
|
||||||
set(FFMPEG_LIBRARIES avcodec.lib avformat.lib swresample.lib avutil.lib avdevice.lib avfilter.lib swscale.lib)
|
set(FFMPEG_LIBRARIES avcodec.lib avformat.lib swresample.lib avutil.lib avdevice.lib avfilter.lib swscale.lib)
|
||||||
target_link_directories(demo_ffmpeg_audio PRIVATE ${FFMPEG_LIB})
|
target_link_directories(demo_ffmpeg_audio PRIVATE ${FFMPEG_LIB})
|
||||||
|
else()
|
||||||
|
set(FFMPEG_LIBRARIES avcodec avformat swresample avutil avdevice avfilter swscale)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(ffmpeg_audio PRIVATE ${FFMPEG_LIBRARIES})
|
target_link_libraries(ffmpeg_audio PRIVATE ${FFMPEG_LIBRARIES})
|
||||||
|
|||||||
@@ -4,6 +4,13 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#include <libavutil/avutil.h>
|
||||||
|
#include <libavutil/channel_layout.h>
|
||||||
|
#include <libavutil/samplefmt.h>
|
||||||
|
#include <libswresample/swresample.h>
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#define fprintf fprintf_s
|
#define fprintf fprintf_s
|
||||||
#endif
|
#endif
|
||||||
@@ -97,6 +104,11 @@ int main(int argc, char **argv)
|
|||||||
infile = argv[1];
|
infile = argv[1];
|
||||||
outfile = argv[2];
|
outfile = argv[2];
|
||||||
|
|
||||||
|
fprintf(stderr, "ffmpeg compiled version: %s\n", fmpg_ffmpeg_version());
|
||||||
|
fprintf(stderr, "ffmpeg runtime version avformat: %d - %s\n", avformat_version(), fmpg_int_version2string(avformat_version()));
|
||||||
|
|
||||||
|
//fprintf(stderr, "ffmpeg runtme config string: %s\n", avformat_configuration());
|
||||||
|
|
||||||
fmpg = fmpg_init();
|
fmpg = fmpg_init();
|
||||||
if (!fmpg) {
|
if (!fmpg) {
|
||||||
fprintf(stderr, "fmpg_init failed\n");
|
fprintf(stderr, "fmpg_init failed\n");
|
||||||
|
|||||||
+110
-12
@@ -31,6 +31,21 @@ extern "C" {
|
|||||||
#include <libswresample/swresample.h>
|
#include <libswresample/swresample.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MSG0(type, msg) fprintf(stderr, type ":");fprintf(stderr, "%s", __FUNCTION__);fprintf(stderr, ": %s\n", msg)
|
||||||
|
#define MSG1(type, msg, a) fprintf(stderr, type ":");fprintf(stderr, "%s", __FUNCTION__);fprintf(stderr, ": " msg "\n", a)
|
||||||
|
#define MSG2(type, msg, a, b) fprintf(stderr, type ":");fprintf(stderr, "%s", __FUNCTION__);fprintf(stderr, ": " msg "\n", a, b)
|
||||||
|
#define MSG3(type, msg, a, b, c) fprintf(stderr, type ":");fprintf(stderr, "%s", __FUNCTION__);fprintf(stderr, ": " msg "\n", a, b, c)
|
||||||
|
|
||||||
|
#define INFO0(msg) MSG0("info", msg)
|
||||||
|
#define INFO1(msg, a) MSG1("info", msg, a)
|
||||||
|
#define INFO2(msg, a, b) MSG2("info", msg, a, b)
|
||||||
|
#define INFO3(msg, a, b, c) MSG3("info", msg, a, b, c)
|
||||||
|
|
||||||
|
#define ERROR0(msg) MSG0("error", msg)
|
||||||
|
#define ERROR1(msg, a) MSG1("error", msg, a)
|
||||||
|
#define ERROR2(msg, a, b) MSG2("error", msg, a, b)
|
||||||
|
#define ERROR3(msg, a, b, c) MSG3("error", msg, a, b, c)
|
||||||
|
|
||||||
static constexpr int FMPG_OUTPUT_BITS = 32;
|
static constexpr int FMPG_OUTPUT_BITS = 32;
|
||||||
static constexpr int FMPG_OUTPUT_BYTES = 4;
|
static constexpr int FMPG_OUTPUT_BYTES = 4;
|
||||||
static constexpr AVSampleFormat FMPG_OUTPUT_FMT = AV_SAMPLE_FMT_S32;
|
static constexpr AVSampleFormat FMPG_OUTPUT_FMT = AV_SAMPLE_FMT_S32;
|
||||||
@@ -150,8 +165,11 @@ static const char *string_c_str(const std::string &s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string get_metadata_string(const AVFormatContext *ctx,
|
static std::string get_metadata_string(const AVFormatContext *ctx,
|
||||||
const char *key)
|
const char *key, bool &has_metadata)
|
||||||
{
|
{
|
||||||
|
if (ctx->metadata == nullptr) { has_metadata = false; return ""; }
|
||||||
|
|
||||||
|
has_metadata = true;
|
||||||
const AVDictionaryEntry *entry = av_dict_get(ctx->metadata,
|
const AVDictionaryEntry *entry = av_dict_get(ctx->metadata,
|
||||||
key,
|
key,
|
||||||
nullptr,
|
nullptr,
|
||||||
@@ -160,8 +178,11 @@ static std::string get_metadata_string(const AVFormatContext *ctx,
|
|||||||
: std::string();
|
: std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_metadata_int(const AVFormatContext *ctx, const char *key)
|
static int get_metadata_int(const AVFormatContext *ctx, const char *key, bool &has_metadata)
|
||||||
{
|
{
|
||||||
|
if (ctx->metadata == nullptr) { has_metadata = false; return -1; }
|
||||||
|
has_metadata = true;
|
||||||
|
|
||||||
const AVDictionaryEntry *entry = av_dict_get(ctx->metadata,
|
const AVDictionaryEntry *entry = av_dict_get(ctx->metadata,
|
||||||
key,
|
key,
|
||||||
nullptr,
|
nullptr,
|
||||||
@@ -239,15 +260,17 @@ static void fill_file_metadata(fmpg_instance *self)
|
|||||||
{
|
{
|
||||||
AVFormatContext *ctx = self->format_ctx;
|
AVFormatContext *ctx = self->format_ctx;
|
||||||
|
|
||||||
|
bool has_metadata;
|
||||||
|
|
||||||
self->file_info.clear();
|
self->file_info.clear();
|
||||||
self->file_info.title = get_metadata_string(ctx, "title");
|
self->file_info.title = get_metadata_string(ctx, "title", has_metadata);
|
||||||
self->file_info.author = get_metadata_string(ctx, "artist");
|
self->file_info.author = get_metadata_string(ctx, "artist", has_metadata);
|
||||||
self->file_info.album = get_metadata_string(ctx, "album");
|
self->file_info.album = get_metadata_string(ctx, "album", has_metadata);
|
||||||
self->file_info.genre = get_metadata_string(ctx, "genre");
|
self->file_info.genre = get_metadata_string(ctx, "genre", has_metadata);
|
||||||
self->file_info.comment = get_metadata_string(ctx, "comment");
|
self->file_info.comment = get_metadata_string(ctx, "comment", has_metadata);
|
||||||
self->file_info.copyright = get_metadata_string(ctx, "copyright");
|
self->file_info.copyright = get_metadata_string(ctx, "copyright", has_metadata);
|
||||||
self->file_info.year = get_metadata_int(ctx, "year");
|
self->file_info.year = get_metadata_int(ctx, "year", has_metadata);
|
||||||
self->file_info.track = get_metadata_int(ctx, "track");
|
self->file_info.track = get_metadata_int(ctx, "track", has_metadata);
|
||||||
self->file_info.bitrate = ctx->bit_rate > 0
|
self->file_info.bitrate = ctx->bit_rate > 0
|
||||||
? static_cast<int>(ctx->bit_rate)
|
? static_cast<int>(ctx->bit_rate)
|
||||||
: -1;
|
: -1;
|
||||||
@@ -360,8 +383,44 @@ static bool init_decoder(fmpg_instance *self)
|
|||||||
return init_codec_context(self) && init_resampler(self);
|
return init_codec_context(self) && init_resampler(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fmpg_compatible_ffmpeg()
|
||||||
|
{
|
||||||
|
int compiled_avformat_major = LIBAVFORMAT_VERSION_MAJOR;
|
||||||
|
int compiled_avcodec_major = LIBAVCODEC_VERSION_MAJOR;
|
||||||
|
int compiled_swresample_major = LIBSWRESAMPLE_VERSION_MAJOR;
|
||||||
|
int compiled_avutil_major = LIBAVUTIL_VERSION_MAJOR;
|
||||||
|
|
||||||
|
int current_avformat_major = AV_VERSION_MAJOR(avformat_version());
|
||||||
|
int current_avcodec_major = AV_VERSION_MAJOR(avcodec_version());
|
||||||
|
int current_swresample_major = AV_VERSION_MAJOR(swresample_version());
|
||||||
|
int current_avutil_major = AV_VERSION_MAJOR(avutil_version());
|
||||||
|
|
||||||
|
auto comp = [](const char *lib, int cv, int rv) {
|
||||||
|
if (cv != rv) {
|
||||||
|
ERROR3("FFMPEG %s Major versions not equal. Compile time: %d, runtime: %d", lib, cv, rv);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
comp("AVFormat", compiled_avformat_major, current_avformat_major);
|
||||||
|
comp("AVCodec", compiled_avcodec_major, current_avcodec_major);
|
||||||
|
comp("SWResample", compiled_swresample_major, current_swresample_major);
|
||||||
|
comp("AVUtil", compiled_avutil_major, current_avutil_major);
|
||||||
|
|
||||||
|
int compatible = (compiled_avformat_major == current_avformat_major) &&
|
||||||
|
(compiled_avcodec_major == current_avcodec_major) &&
|
||||||
|
(compiled_swresample_major == current_swresample_major) &&
|
||||||
|
(compiled_avutil_major == current_avutil_major);
|
||||||
|
|
||||||
|
return compatible;
|
||||||
|
}
|
||||||
|
|
||||||
fmpg_instance *fmpg_init(void)
|
fmpg_instance *fmpg_init(void)
|
||||||
{
|
{
|
||||||
|
if (!fmpg_compatible_ffmpeg()) {
|
||||||
|
ERROR0("Compiled major ffmpeg version ≃ runtime major version, not compatible.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new fmpg_instance();
|
return new fmpg_instance();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
@@ -380,14 +439,21 @@ int fmpg_open_file(fmpg_instance *instance, const char *filename)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avformat_open_input(&instance->format_ctx,
|
if (instance->format_ctx != nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int r = avformat_open_input(&instance->format_ctx,
|
||||||
filename,
|
filename,
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr) < 0) {
|
nullptr);
|
||||||
|
if (r < 0) {
|
||||||
fmpg_close(instance);
|
fmpg_close(instance);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "metadata: %p", instance->format_ctx->metadata);
|
||||||
|
|
||||||
if (avformat_find_stream_info(instance->format_ctx, nullptr) < 0) {
|
if (avformat_find_stream_info(instance->format_ctx, nullptr) < 0) {
|
||||||
fmpg_close(instance);
|
fmpg_close(instance);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -923,3 +989,35 @@ double fmpg_timecode(fmpg_instance *instance)
|
|||||||
{
|
{
|
||||||
return instance ? instance->decoder.timecode : 0.0;
|
return instance ? instance->decoder.timecode : 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *fmpg_ffmpeg_version()
|
||||||
|
{
|
||||||
|
static char *version = nullptr;
|
||||||
|
|
||||||
|
if (version == nullptr) {
|
||||||
|
version = static_cast<char *>(malloc(1024));
|
||||||
|
}
|
||||||
|
sprintf(version, "avformat: %u.%u.%u (%d), avcodec: %u.%u.%u (%d), swresample: %u.%u.%u (%d), avutil: %u.%u.%u (%d)",
|
||||||
|
LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO, LIBAVFORMAT_VERSION_INT,
|
||||||
|
LIBAVCODEC_VERSION_MAJOR, LIBAVCODEC_VERSION_MINOR, LIBAVCODEC_VERSION_MICRO, LIBAVCODEC_VERSION_INT,
|
||||||
|
LIBSWRESAMPLE_VERSION_MAJOR, LIBSWRESAMPLE_VERSION_MINOR, LIBSWRESAMPLE_VERSION_MICRO, LIBSWRESAMPLE_VERSION_INT,
|
||||||
|
LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO, LIBAVUTIL_VERSION_INT
|
||||||
|
);
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *fmpg_int_version2string(int ver)
|
||||||
|
{
|
||||||
|
static char *version = nullptr;
|
||||||
|
|
||||||
|
if (version == nullptr) {
|
||||||
|
version = static_cast<char *>(malloc(1024));
|
||||||
|
}
|
||||||
|
|
||||||
|
int major = AV_VERSION_MAJOR(ver);
|
||||||
|
int minor = AV_VERSION_MINOR(ver);
|
||||||
|
int micro = AV_VERSION_MICRO(ver);
|
||||||
|
sprintf(version, "%u.%u.%u", major, minor, micro);
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|||||||
@@ -150,6 +150,10 @@ FFMPEG_EXTERN int64_t fmpg_sample_position(fmpg_instance *instance);
|
|||||||
/* Approximate start time of the current decoded block in seconds. */
|
/* Approximate start time of the current decoded block in seconds. */
|
||||||
FFMPEG_EXTERN double fmpg_timecode(fmpg_instance *instance);
|
FFMPEG_EXTERN double fmpg_timecode(fmpg_instance *instance);
|
||||||
|
|
||||||
|
FFMPEG_EXTERN const char *fmpg_ffmpeg_version();
|
||||||
|
FFMPEG_EXTERN const char *fmpg_int_version2string(int ver);
|
||||||
|
FFMPEG_EXTERN int fmpg_compatible_ffmpeg();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user