From 31e0642e6498e1b06f44468980322a842099d43e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 8 Jul 2018 22:43:01 -0700 Subject: [PATCH] FFmpeg: Support libswresample (fixes #1120) --- CHANGES | 1 + CMakeLists.txt | 29 ++++++++++++++++++++++------- src/feature/ffmpeg/ffmpeg-encoder.c | 28 +++++++++++++++++++++++++++- src/feature/ffmpeg/ffmpeg-encoder.h | 4 ++++ 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 89a7ab176..4f2e65fe1 100644 --- a/CHANGES +++ b/CHANGES @@ -67,6 +67,7 @@ Misc: - GB, GBA Audio: Increase max audio volume - GB: Fix VRAM/palette locking (fixes mgba.io/i/1109) - GB Video: Darken colors in GBA mode + - FFmpeg: Support libswresample (fixes mgba.io/i/1120, mgba.io/b/123) 0.6.3: (2017-04-14) Bugfixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index d3f0c353e..7c2efeb98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -438,7 +438,7 @@ set(WANT_LIBZIP ${USE_LIBZIP}) set(WANT_SQLITE3 ${USE_SQLITE3}) set(USE_CMOCKA ${BUILD_SUITE}) -find_feature(USE_FFMPEG "libavcodec;libavformat;libavresample;libavutil;libswscale") +find_feature(USE_FFMPEG "libavcodec;libavformat;libavutil;libswscale") find_feature(USE_ZLIB "ZLIB") find_feature(USE_MINIZIP "minizip") find_feature(USE_PNG "PNG") @@ -450,6 +450,13 @@ find_feature(USE_SQLITE3 "sqlite3") find_feature(USE_ELF "libelf") find_feature(ENABLE_PYTHON "PythonLibs") +if(USE_FFMPEG) + set(USE_LIBAVRESAMPLE ON) + set(USE_LIBSWRESAMPLE ON) + find_feature(USE_LIBAVRESAMPLE "libavresample") + find_feature(USE_LIBSWRESAMPLE "libswresample") +endif() + # Features set(DEBUGGER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/debugger.c @@ -485,22 +492,30 @@ source_group("Debugger" FILES ${DEBUGGER_SRC}) if(USE_FFMPEG) list(APPEND FEATURES FFMPEG) - pkg_search_module(LIBSWRESAMPLE QUIET libswresample) - if(NOT LIBSWRESAMPLE_FOUND) + if(USE_LIBSWRESAMPLE) + list(APPEND FEATURES LIBSWRESAMPLE) + else() + list(APPEND FEATURES LIBAVRESAMPLE) list(APPEND FEATURES LIBAV) endif() - include_directories(AFTER ${LIBAVCODEC_INCLUDE_DIRS} ${LIBAVFORMAT_INCLUDE_DIRS} ${LIBAVRESAMPLE_INCLUDE_DIRS} ${LIBAVUTIL_INCLUDE_DIRS} ${LIBSWSCALE_INCLUDE_DIRS}) - link_directories(${LIBAVCODEC_LIBRARY_DIRS} ${LIBAVFORMAT_LIBRARY_DIRS} ${LIBAVRESAMPLE_LIBRARY_DIRS} ${LIBAVUTIL_LIBRARY_DIRS} ${LIBSWSCALE_LIBRARY_DIRS}) + include_directories(AFTER ${LIBAVCODEC_INCLUDE_DIRS} ${LIBAVFORMAT_INCLUDE_DIRS} ${LIBAVRESAMPLE_INCLUDE_DIRS} ${LIBAVUTIL_INCLUDE_DIRS} ${LIBSWRESAMPLE_INCLUDE_DIRS} ${LIBSWSCALE_INCLUDE_DIRS}) + link_directories(${LIBAVCODEC_LIBRARY_DIRS} ${LIBAVFORMAT_LIBRARY_DIRS} ${LIBAVRESAMPLE_LIBRARY_DIRS} ${LIBAVUTIL_LIBRARY_DIRS} ${LIBSWRESAMPLE_LIBRARY_DIRS} ${LIBSWSCALE_LIBRARY_DIRS}) list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/ffmpeg/ffmpeg-encoder.c") string(REGEX MATCH "^[0-9]+" LIBAVCODEC_VERSION_MAJOR ${libavcodec_VERSION}) string(REGEX MATCH "^[0-9]+" LIBAVFORMAT_VERSION_MAJOR ${libavformat_VERSION}) string(REGEX MATCH "^[0-9]+" LIBAVRESAMPLE_VERSION_MAJOR ${libavresample_VERSION}) string(REGEX MATCH "^[0-9]+" LIBAVUTIL_VERSION_MAJOR ${libavutil_VERSION}) + string(REGEX MATCH "^[0-9]+" LIBSWRESAMPLE_VERSION_MAJOR ${libswresample_VERSION}) string(REGEX MATCH "^[0-9]+" LIBSWSCALE_VERSION_MAJOR ${libswscale_VERSION}) - list(APPEND DEPENDENCY_LIB ${LIBAVCODEC_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVRESAMPLE_LIBRARIES} ${LIBAVUTIL_LIBRARIES} ${LIBSWSCALE_LIBRARIES}) + math(EXPR LIBSWRESAMPLE_VERSION_DEBIAN "${LIBSWRESAMPLE_VERSION_MAJOR} - 1") + list(APPEND DEPENDENCY_LIB ${LIBAVCODEC_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVRESAMPLE_LIBRARIES} ${LIBAVUTIL_LIBRARIES} ${LIBSWSCALE_LIBRARIES} ${LIBSWRESAMPLE_LIBRARIES}) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavcodec${LIBAVCODEC_VERSION_MAJOR}|libavcodec-extra-${LIBAVCODEC_VERSION_MAJOR}|libavcodec-ffmpeg${LIBAVCODEC_VERSION_MAJOR}|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}") set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavformat${LIBAVFORMAT_VERSION_MAJOR}|libavformat-ffmpeg${LIBAVFORMAT_VERSION_MAJOR}") - set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavresample${LIBAVRESAMPLE_VERSION_MAJOR}|libavresample-ffmpeg${LIBAVRESAMPLE_VERSION_MAJOR}") + if(USE_LIBSWRESAMPLE) + set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswresample${LIBSWRESAMPLE_VERSION_DEBIAN}|libswresample-ffmpeg${LIBSWRESAMPLE_VERSION_DEBIAN}") + else() + set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavresample${LIBAVRESAMPLE_VERSION_MAJOR}|libavresample-ffmpeg${LIBAVRESAMPLE_VERSION_MAJOR}") + endif() set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavutil${LIBAVUTIL_VERSION_MAJOR}|libavutil-ffmpeg${LIBAVUTIL_VERSION_MAJOR}") set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswscale${LIBSWSCALE_VERSION_MAJOR}|libswscale-ffmpeg${LIBSWSCALE_VERSION_MAJOR}") set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libavcodec-extra|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}") diff --git a/src/feature/ffmpeg/ffmpeg-encoder.c b/src/feature/ffmpeg/ffmpeg-encoder.c index 2da3754f8..7fbc9a51c 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.c +++ b/src/feature/ffmpeg/ffmpeg-encoder.c @@ -19,7 +19,11 @@ #include #include +#ifdef USE_LIBAVRESAMPLE #include +#else +#include +#endif #include static void _ffmpegPostVideoFrame(struct mAVStream*, const color_t* pixels, size_t stride); @@ -248,6 +252,7 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { encoder->audioFrame->nb_samples = encoder->audio->frame_size; encoder->audioFrame->format = encoder->audio->sample_fmt; encoder->audioFrame->pts = 0; +#ifdef USE_LIBAVRESAMPLE encoder->resampleContext = avresample_alloc_context(); av_opt_set_int(encoder->resampleContext, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0); av_opt_set_int(encoder->resampleContext, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); @@ -256,6 +261,11 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { av_opt_set_int(encoder->resampleContext, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0); av_opt_set_int(encoder->resampleContext, "out_sample_fmt", encoder->sampleFormat, 0); avresample_open(encoder->resampleContext); +#else + encoder->resampleContext = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO, encoder->sampleFormat, encoder->sampleRate, + AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, PREFERRED_SAMPLE_RATE, 0, NULL); + swr_init(encoder->resampleContext); +#endif encoder->audioBufferSize = (encoder->audioFrame->nb_samples * PREFERRED_SAMPLE_RATE / encoder->sampleRate) * 4; encoder->audioBuffer = av_malloc(encoder->audioBufferSize); encoder->postaudioBufferSize = av_samples_get_buffer_size(0, encoder->audio->channels, encoder->audio->frame_size, encoder->audio->sample_fmt, 0); @@ -362,7 +372,11 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) { avcodec_close(encoder->audio); if (encoder->resampleContext) { +#ifdef USE_LIBAVRESAMPLE avresample_close(encoder->resampleContext); +#else + swr_free(&encoder->resampleContext); +#endif } if (encoder->absf) { @@ -414,10 +428,11 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right } int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt); + encoder->currentAudioSample = 0; +#ifdef USE_LIBAVRESAMPLE avresample_convert(encoder->resampleContext, 0, 0, 0, (uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4); - encoder->currentAudioSample = 0; if (avresample_available(encoder->resampleContext) < encoder->audioFrame->nb_samples) { return; } @@ -425,6 +440,17 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right av_frame_make_writable(encoder->audioFrame); #endif int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize); +#else +#if LIBAVCODEC_VERSION_MAJOR >= 55 + av_frame_make_writable(encoder->audioFrame); +#endif + if (swr_get_out_samples(encoder->resampleContext, encoder->audioBufferSize / 4) < encoder->audioFrame->nb_samples) { + swr_convert(encoder->resampleContext, NULL, 0, (const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4); + return; + } + int samples = swr_convert(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize, + (const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4); +#endif encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame, encoder->audio->time_base, encoder->audioStream->time_base); encoder->currentAudioFrame += samples; diff --git a/src/feature/ffmpeg/ffmpeg-encoder.h b/src/feature/ffmpeg/ffmpeg-encoder.h index feeeddc77..f8c5add36 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.h +++ b/src/feature/ffmpeg/ffmpeg-encoder.h @@ -57,7 +57,11 @@ struct FFmpegEncoder { size_t currentAudioSample; int64_t currentAudioFrame; int64_t nextAudioPts; // TODO (0.6): Remove +#ifdef USE_LIBAVRESAMPLE struct AVAudioResampleContext* resampleContext; +#else + struct SwrContext* resampleContext; +#endif #ifdef FFMPEG_USE_NEW_BSF struct AVBSFContext* absf; // Needed for AAC in MP4 #else